Question

I'm trying to run some initialization code before a test. I've tried the suggestions in other questions, but it doesn't seem to work. My domain model raises events via the following class:

public static class DomainEvents
{
    private static readonly object @lock = new object();
    private static Action<IDomainEvent> raiseEvent;

    public static void Raise<TEvent>(TEvent @event) where TEvent : class, IDomainEvent 
    {
         // omitted for brevity
    }

    public static void RegisterEventPublisher(Action<IDomainEvent> eventPublisher)
    {
        lock (@lock)
        {
            raiseEvent = eventPublisher;
        }
    }
}

For testing purposes I would like to capture these events in a static list. What is the best way of doing this?

Update

The problem was caused by the order in which the tests are run (which as Alexander points out below, is not guaranteed). In one of my specs I had registered a mock event publisher. The fact that the spec would often run in different orders meant that a) to begin with I didn't know I had the issue (the "problem" spec always ran last) and b) Once I started having the issue, the number of failing tests would often vary between runs (making it even more confusing).

The lesson learned - clean up any static resources after each context has run. You can do this by implementing ICleanupAfterEveryContextInAssembly.

Was it helpful?

Solution

This works for me:

using System;
using System.Collections.Generic;

using Machine.Specifications;

namespace AssemblyContextSpecs
{
  public static class DomainEvents
  {
    static readonly object @lock = new object();

    static Action<IDomainEvent> raiseEvent;

    public static void Raise<TEvent>(TEvent @event) where TEvent : class, IDomainEvent
    {
      raiseEvent(@event);
    }

    public static void RegisterEventPublisher(Action<IDomainEvent> eventPublisher)
    {
      lock (@lock)
      {
        raiseEvent = eventPublisher;
      }
    }
  }

  public interface IDomainEvent
  {
  }

  class FooEvent : IDomainEvent
  {
  }

  public class DomainEventsContext : IAssemblyContext
  {
    internal static IList<IDomainEvent> Events = new List<IDomainEvent>();

    public void OnAssemblyStart()
    {
      DomainEvents.RegisterEventPublisher(x => Events.Add(x));
    }

    public void OnAssemblyComplete()
    {
    }
  }

  public class When_a_domain_event_is_raised
  {
    Because of = () => DomainEvents.Raise(new FooEvent());

    It should_capture_the_event =
      () => DomainEventsContext.Events.ShouldContain(x => x.GetType() == typeof(FooEvent));
  }
}

Shouldn't RegisterEventPublisher rather be RegisterEventSubscriber?

OTHER TIPS

Maybe I'm misunderstanding the issue, but the basic pattern is:

public class WhenSomeDomainEventIsRaised
{
    private IList<IDomainEvent> EventsRaised = new List<IDomainEvent>();

    Establish context = () => 
    {
        // subscribe to events; when raised, add to EventsRaised list
    }
}

If you want to do this for all tests or a subset of tests:

public abstract class DomainSpecification
{
    protected IList<IDomainEvent> EventsRaised = new List<IDomainEvent>();

    Establish context = () => 
    {
        // subscribe to events; when raised, add to EventsRaised list
    }
}

You can have all specs that need this behaviour inherit from this class, and MSpec will take care of running all Establish blocks along the inheritance hierarchy.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top