我如何测试一个工厂的课程?

svr*_*ist 4 c# moq autofixture

我正在尝试测试一个带有工厂(Func<T>)的类,我正在使用Moq和AutoFixture.

设置"环境"以查看工厂是否已被使用以及在返回的实例上使用了多少次以及使用了哪些方法的最佳方法是什么?

目前,我Mock'ing的TInjecting一个Func<T>用于跟踪所有返回的模拟实例的数量:

public class SystemUnderTest {
    public SystemUnderTest(Func<IMyClass> c)
    {
        try {
            var c1 = c();
            c1.Name="n1";
            c1.Send();
        }
        catch(Exception){
            var c2 = c();
            c2.Name="n2";
            c2.Send();
        }
    }
}
private Mock<IMyClass> MockFactory()
{
   var m = new Mock<IMyClass>();
   m.SetupProperty(mc=>mc.Name);
   _returnedStubs.Add(m);
   return m;
}  
[Test]
public void TestSomething()
{
    var fixture = new Fixture();
    fixture.Inject(()=>MockFactory().Object)
    var sut = fixture.CreateAnonymous<SystemUnderTest>();
    Assert.That(_returnedStubs.Count,Is.Equal(1));
    _returnedStubs[0].Verify(m=>m.Send(),Times.Exactly(1));
    _returnedStubs[0].Verify(m=>m.Name = "n1");
}
Run Code Online (Sandbox Code Playgroud)

但对我来说,感觉有点不好/丑陋.而且我很确定测试类中的实例变量是危险的

Enr*_*lio 7

由于AutoFixture 能够创建匿名委托,当被要求创建匿名委托SystemUnderTest,它还会自动提供匿名Func<IMyClass>委托,而匿名委托又会IMyClass在调用时返回匿名委托.

这意味着,在这种情况下:

public class SystemUnderTest
{
    public SystemUnderTest(Func<IMyClass> c)
    {
        try
        {
            var c1 = c();
            // ...
        }
        catch (Exception)
        {
            var c2 = c();
            // ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下代码:

var fixture = new Fixture();
var sut = fixture.CreateAnonymous<SystemUnderTest>();
Run Code Online (Sandbox Code Playgroud)

将使用匿名实例分配c1c2变量IMyClass.此外,如果将AutoFixture配置为自动模拟容器(例如使用)AutoMoqCustomization,那些匿名实例也IMyClass将恰好是Moq代理:

var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization());
var sut = fixture.CreateAnonymous<SystemUnderTest>();
Run Code Online (Sandbox Code Playgroud)

但是,这些信息尽管很有用,在您的特定情况下并不能真正帮助您,因为您需要获取Func<IMyClass>测试中工厂方法返回的模拟对象,以便配置它们的行为并对它们的方式做出一些断言.已经与之互动.

最好的解决办法,在我看来,是工厂方法的实施改变Func<IMyClass>一个接口.这样,你可以创建一个假的工厂,返回不同的嘲笑了的IMyClass时候的接口Create方法是在顺序调用多次:

所以,给出这个例子:

public interface IFactory<T>
{
    T Create();
}

public class SystemUnderTest
{
    public SystemUnderTest(IFactory<IMyClass> factory)
    {
        try
        {
            var c1 = factory.Create();
            // ...
        }
        catch (Exception)
        {
            var c2 = factory.Create();
            // ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以按如下方式设置测试场景:

    // Given
    var c1 = new Mock<IMyClass>();
    var c2 = new Mock<IMyClass>();
    // TODO: setup the behavior of the mock objects
    var factory = new Mock<IFactory<IMyClass>>();
    factory.Setup(s => s.Create()).ReturnsInOrder(c1.Object, c2.Object);

    // When
    var fixture = new Fixture();
    fixture.Inject(() => factory.Object)
    var sut = fixture.CreateAnonymous<SystemUnderTest>();

    // Then
    // TODO: verify the expectations on the mock objects
Run Code Online (Sandbox Code Playgroud)

请注意,这ReturnsInOrder是一个自定义扩展方法,它使用CallbackMoq中的方法在连续多次调用时从存根方法返回不同的值.