确定。我试图解决为什么MSpec使用静态方法/变量。 (不完全是静态方法,但是使用成员变量委托,它实际上是相同的。)
这使得无法重用上下文。那或通过并确保手动重置所有静态变量。这对测试隔离没有强制执行。如果一个测试设置了一些变量,而下一个测试就检查了它,那么当它不应该传递时,它会通过。
这开始变得非常烦人。我在一个“因为”语句中所做的只是留在那里,而不是仅仅因为它共享相同的上下文而进行其他随机测试。
编辑 -
问题是,我如何“执行”测试隔离。例如,请查看下面的规范,分享FooContext
。如果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();
}
答案 0 :(得分:2)
这是技术上和历史上的限制。
至于上下文共享/上下文基类:从您所声明的情况来看,您似乎过度使用了这个概念。您应该始终初始化Establish中的静态字段,因此全局状态将成为非问题。应该充分考虑上下文共享,因此,引用它,它不会随机发生。尝试使用辅助方法进行复杂的设置,并在Establishs中更详细(我会说明确)。它将有助于使您的规范更具可读性。
答案 1 :(得分:1)
我基本上将一个自动插入容器放在夹具的实例中,并确保为每个规格重新创建夹具。如果需要其他一些设置,只需继承或添加到灯具。
(注意这会使用结构图和结构图/ moq / automocking容器。我相信它对于不同的容器/模拟框架都是一样的。)
/// <summary>
/// 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)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// 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.
/// </remarks>
public abstract class BaseSpec<T>
where T : class
{
public static TestFixture Fixture;
private Establish a_new_context = () =>
{
Fixture = new TestFixture();
MockedTypes = new Dictionary<Type, Action>();
};
/// <summary>
/// This dictionary holds a list of mocks that need to be verified by the behavior.
/// </summary>
private static Dictionary<Type, Action> MockedTypes;
/// <summary>
/// Gets the mock of a requested type, and it creates a verify method that is used
/// in the "AllMocksVerified" behavior.
/// </summary>
/// <typeparam name="TMock"></typeparam>
/// <returns></returns>
public static Mock<TMock> GetMock<TMock>()
where TMock : class
{
var mock = Mock.Get(Fixture.Context.Get<TMock>());
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<T> Context { get; private set; }
public T TestTarget
{
get { return Context.ClassUnderTest; }
}
public TestFixture()
{
Context = new MoqAutoMocker<T>();
}
}
}
这是一个示例用法。
public class get_existing_goo : BaseSpec<ClassToTest>
{
private static readonly Goo Param = new Goo();
private Establish goo_exist =
() => GetMock<Foo>()
.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。