Search code examples
c#.netmspec

How do I run code before every test run in MSpec?


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.


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?