【问题标题】:How do I run code before every test run in MSpec?在 MSpec 中每次测试运行之前如何运行代码?
【发布时间】:2012-04-18 20:10:05
【问题描述】:

我正在尝试在测试之前运行一些初始化代码。我试过suggestions in other questions,但它似乎不起作用。我的领域模型通过以下类引发事件:

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;
        }
    }
}

出于测试目的,我想在静态列表中捕获这些事件。这样做的最佳方法是什么?

更新

问题是由运行测试的顺序引起的(正如 Alexander 在下面指出的,不能保证)。在我的一个规范中,我注册了一个模拟事件发布者。规范经常以不同的顺序运行的事实意味着 a) 一开始我不知道我有问题(“问题”规范总是最后运行)和 b) 一旦我开始遇到问题,失败的测试在运行之间通常会有所不同(使其更加混乱)。

经验教训 - 在每个上下文运行后清理所有静态资源。你可以通过实现ICleanupAfterEveryContextInAssembly来做到这一点。

【问题讨论】:

  • 您是否养成了使用保留字作为变量名的习惯?可能不是一个好主意。
  • 并非如此。虽然我过去从未遇到过任何问题。

标签: c# .net mspec


【解决方案1】:

也许我误解了这个问题,但基本模式是:

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

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

如果您想对所有测试或测试子集执行此操作:

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

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

您可以让所有需要此行为的规范从此类继承,MSpec 将负责沿继承层次运行所有 Establish 块。

【讨论】:

    【解决方案2】:

    This 为我工作:

    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));
      }
    }
    

    不应该是RegisterEventPublisher 而应该是RegisterEventSubscriber?

    【讨论】:

    • 这对我来说适用于单一规范。我似乎遇到的问题是在运行多个触发事件的规范时。我有大约 20 个触发事件的规范,但列表中只有 5 个事件。也许这是一个线程问题?
    • MSpec 不会自己创建线程。目前只有一个限制w.r.t。 ReSharper,即IAssemblyContext 针对每个上下文运行。它们通常每个程序集(TD.Net、控制台)运行一次。
    • 这可能是订购问题吗? MSpec 不保证上下文执行的顺序,因此可能不是“所有以前的”事件都在列表中。
    • 我不一定关心顺序,只要我可以在每个规范运行之前清除列表。这个问题似乎确实存在于多个上下文中。当我删除除一个上下文(包含引发事件的 5 个规范)之外的所有上下文时,它们都成功了。当我重新添加上下文时,同样的 5 个失败。
    • 你用什么跑步者?你能发布一些重现问题的代码吗?
    猜你喜欢
    • 2014-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    • 1970-01-01
    • 2016-10-18
    • 1970-01-01
    • 2014-05-02
    相关资源
    最近更新 更多