当需要静态成员/方法时,如何在MSpec中强制执行测试隔离?

好。 我试图解决为什么MSpec使用静态方法/变量。 (不完全是静态方法,但是使用成员变量委托,它实际上是相同的)。

这使得无法重用上下文。 那或通过并确保手动重置所有静态变量。 这在测试隔离上没有强制执行。 如果一个测试设置了一些变量而下一个测试就检查了它,那么当它不应该传递时它会通过。

这开始变得非常烦人。 我在一个“因为”语句中所做的只是留在那里,而不是仅仅因为它共享相同的上下文而进行到所有其他随机测试。

编辑-

问题是,我如何“ENFORCE”测试隔离。 例如,查看下面的规范,共享FooContext 。 如果should_not_throw通过,我们会猜测should_not_throw吗?

 public class FooContext { Establish context = () => Subject = new Foo(); public static Foo Subject; public static int result; public static Exception ex; } public class When_getting_an_int_incorrectly : FooContext { Because of = () => ex = Exception.Catch(() => result = Subject.GetInt(null)); It should_throw = () => ex.ShouldNotBeNull(); } public class When_getting_an_int_correctly : FooContext { Because of = () => ex = Exception.Catch(() => result = Subject.GetInt(0)); It should_not_throw = () => ex.ShouldBeNull(); } 

这是一个技术上和历史上的限制。

  • 您需要静态字段来在代理之间共享信息(建立,因为,它,清理)。
  • MSpec试图模仿rspec,所以我认为Aaron认为代表是合适的并且发布了你今天在2008年或2009年看到的语法。这种语法今天仍然存在。

至于上下文共享/上下文基类:从你所说的看起来你似乎过度使用了这个概念。 您应该始终初始化Establish中的静态字段,因此全局状态将变为非问题。 应该充分考虑上下文共享,因此,引用它,它不会随机发生。 尝试使用辅助方法进行复杂的设置,并在Establishs中更详细(我会说明确)。 它将有助于使您的规格更具可读性。

瞧,看哪。 我想提出我的(部分)解决问题的方法(强制执行夹具和设置隔离)。 那和同时解决管道代码的问题。

我基本上将一个自动插入容器放在夹具的实例中,并确保为每个规格重新创建夹具。 如果需要其他一些设置,只需inheritance或添加到夹具。

(注意这使用了结构图和结构图/ moq / automocking容器。我确信它对于不同的容器/模拟框架都是一样的。)

 ///  /// This is a base class for all the specs. Note this spec is NOT thread safe. (But then /// I don't see MSpec running parallel tests anyway) ///  ///  ///  /// This class provides setup of a fixture which contains a) access to class under test /// b) an auto mocking container and c) enforce a clean fixture for every spec. ///  public abstract class BaseSpec where T : class { public static TestFixture Fixture; private Establish a_new_context = () => { Fixture = new TestFixture(); MockedTypes = new Dictionary(); }; ///  /// This dictionary holds a list of mocks that need to be verified by the behavior. ///  private static Dictionary MockedTypes; ///  /// Gets the mock of a requested type, and it creates a verify method that is used /// in the "AllMocksVerified" behavior. ///  ///  ///  public static Mock GetMock() where TMock : class { var mock = Mock.Get(Fixture.Context.Get()); if (!MockedTypes.ContainsKey(typeof(TMock))) MockedTypes.Add(typeof(TMock), mock.VerifyAll); return mock; } [Behaviors] public class AllMocksVerified { private Machine.Specifications.It should_verify_all = () => { foreach (var mockedType in MockedTypes) { mockedType.Value(); } }; } public class TestFixture { public MoqAutoMocker Context { get; private set; } public T TestTarget { get { return Context.ClassUnderTest; } } public TestFixture() { Context = new MoqAutoMocker(); } } } 

这是一个示例用法。

  public class get_existing_goo : BaseSpec { private static readonly Goo Param = new Goo(); private Establish goo_exist = () => GetMock() .Setup(a => a.MockMethod()) .Returns(Param); private static Goo result; private Because goo_is_retrieved = () => result = Fixture.Context.ClassUnderTest.MethodToTest(); private It should_not_be_null = () => result.ShouldEqual(Param); } 

基本上,如果需要共享某些内容,请将其放在fixture本身的实例中。 这“强制”分离……一些什么。

在这方面我仍然更喜欢Xunit。