Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何测试一个使用工厂的类?_C#_Moq_Autofixture - Fatal编程技术网

C# 如何测试一个使用工厂的类?

C# 如何测试一个使用工厂的类?,c#,moq,autofixture,C#,Moq,Autofixture,我试图测试一个采用工厂(Func)的类,并且使用Moq和AutoFixture 设置“环境”的最佳方法是什么,以查看工厂是否已被使用,以及在返回的实例上使用了多少次和哪些方法 目前,我正在模拟T和injectionaFunc,它记录所有返回的模拟实例的计数: public class SystemUnderTest { public SystemUnderTest(Func<IMyClass> c) { try { var c1

我试图测试一个采用工厂(
Func
)的类,并且使用Moq和AutoFixture

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

目前,我正在模拟
T
injection
a
Func
,它记录所有返回的模拟实例的计数:

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");
}
公共类系统测试中{
被测公共系统(功能c)
{
试一试{
var c1=c();
c1.Name=“n1”;
c1.Send();
}
捕获(例外){
var c2=c();
c2.Name=“n2”;
c2.Send();
}
}
}
私有模拟工厂()
{
var m=新的Mock();
m、 SetupProperty(mc=>mc.Name);
_返回的存根。添加(m);
返回m;
}  
[测试]
公共空间
{
var fixture=新fixture();
fixture.Inject(()=>MockFactory().Object)
var sut=fixture.CreateAnonymous();
断言(_returnedStubs.Count,Is.Equal(1));
_returnedStubs[0]。验证(m=>m.Send(),Times.justice(1));
_返回的存根[0]。验证(m=>m.Name=“n1”);
}

但我觉得有点不确定/难看。我很确定测试类中的实例变量是一件危险的事情

最好的方法之一是创建自己的函数并传递它,然后在那里设置期望值或状态

int numberOfTimesUsed = 0;
myObject.Func = (x) => 
{ 
   numberOfTimesUsed++;
   Assert.IsNotNull(x); // checks if x passed was not null
};

...

Assert.AreEqual(2, numberOfTimesUsed); // checks if called twice
由于AutoFixture是,当被要求创建测试下系统的匿名实例时,它还将自动提供一个匿名的
Func
委托,该委托在调用时反过来返回
IMyClass
的匿名实例

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

public class SystemUnderTest
{
    public SystemUnderTest(Func<IMyClass> c)
    {
        try
        {
            var c1 = c();
            // ...
        }
        catch (Exception)
        {
            var c2 = c();
            // ...
        }
    }
}
然而,这些信息虽然有用,但在特定情况下并不能真正帮助您,因为您需要掌握测试中
Func
factory方法返回的模拟对象,以便配置它们的行为并对它们的交互方式做出一些断言

在我看来,最好的解决方案是将工厂方法的实现
Func
更改为接口。这样,当
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();
            // ...
        }
    }
}
公共接口IFactory
{
T Create();
}
公共类系统未测试
{
被测公共系统(IFactory工厂)
{
尝试
{
var c1=factory.Create();
// ...
}
捕获(例外)
{
var c2=factory.Create();
// ...
}
}
}
您可以按如下方式设置测试场景:

    // 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
//给定
var c1=新的Mock();
var c2=新的Mock();
//TODO:设置模拟对象的行为
var factory=new Mock();
factory.Setup(s=>s.Create()).returnsOrder(c1.Object,c2.Object);
//什么时候
var fixture=新fixture();
fixture.Inject(()=>factory.Object)
var sut=fixture.CreateAnonymous();
//然后
//TODO:验证对模拟对象的期望

请注意,
ReturnsInOrder
是一个在Moq中使用
Callback
方法的函数,当存根方法在一行中被多次调用时,它将从存根方法返回不同的值

虽然我意识到这可能是试图将问题缩小到可管理的范围,但我想知道你想做什么?MyClass是一个具体的类吗?为什么它被创建了多少次才重要呢?这很有趣,因为
IMyClass
是一个用于收集数据并在Web服务上发送数据的类。但是,如果为IMyClass的第一个实例收集数据时抛出异常,则会创建另一个实例,其中包含一条特定消息,我想验证:“当instance1抛出异常时:1)instance2已创建,2)instance2获取第一个异常的消息,3)不应再创建任何实例”我明白了。然而,imyclassapi中存在时间耦合,这使得测试更加困难。如果您更改API以启用类似
c1.Send(“n1”)。。。这个特定的反馈实际上与AutoFixture无关,但它也会使这变得更容易。
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();
            // ...
        }
    }
}
    // 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