单元测试时在方法内部创建模拟对象

本文关键字:创建 模拟 对象 内部 方法 单元测试 | 更新日期: 2023-09-27 18:14:43

我有一个下面的场景:

public void DoSomething(...)
{
   ...
   ClassA obj1 = new ClassA();
   ClassB obj2 = new ClassB();
   ClassC obj3 = new ClassC();
   ...
}

我明白,如果我使用依赖倒置原则,我可以模拟接口,它将工作。问题是这些实例只在这个方法中需要,所以在类级别上创建它们没有意义。最重要的是,有些方法有5-6个对象声明,因此将这些作为参数传递会使参数列表膨胀。

无论如何我可以使用Moq, NMock等模拟这些类(这是基于一个共同的接口)?

单元测试时在方法内部创建模拟对象

如果您只需要在方法中实例化这些类型,那么您就不应该将它们注入到类中。

相反,典型的解决方案是注入一个允许你的方法解析接口的工厂。

通常的方法是:

interface IMessageFactory
{
    IMessage CreateMessage(string text);   
}
class ConcreteMessageFactory : IMessageFactory
{
    IMessage CreateMessage(string text) { return new Message(text); }
}
class MockMessageFactory : IMessageFactory
{
    public IMessage CreateMessage(string text) { return new MockMessage(text); }
}

class MyClient
{
    private readonly IMessageFactory _msgFactory;
    public MyClient(IMessageFactory msgFactory)
    {
        _msgFactory = msgFactory;   
    }
    public void SendMessage(string text)
    {
        IMessage msg = _msgFactory.CreateMessage(text);
        msg.Send();
    }
}

//Usage:
new MyClient(new ConcreteMessageFactory());
//Mocking
new MyClient(new MockMessageFactory());

根据您使用的DI框架,该框架可以使这种方式更容易。例如,Castle Windsor允许您注入基于委托的工厂,而不是上面的基于接口的工厂:

class MyClient
{
    private readonly Func<string, IMessage> _msgFactory;
    public MyClient(Func<string, IMessage> msgFactory)
    {
        _msgFactory = msgFactory;   
    }
    public void SendMessage(string text)
    {
        IMessage msg = _msgFactory(text);
        msg.Send();
    }
}
//Usage:
new MyClient(() => new Message());
//Mocking
new MyClient(() => new MockMessage());

您可以使用StructureMap之类的东西来为您做这项工作,您的方法将变成类似于。

public void DoSomething(...)
{
   ...
   IClassA obj1 = ObjectFactory.GetInstance<IClassA>();
   IClassB obj2 = ObjectFactory.GetInstance<IClassB>();
   IClassC obj3 = ObjectFactory.GetInstance<IClassC>();
   ...
}

通常你会像这样在全局中映射它们。

        ObjectFactory.Initialize(x =>
        {
            x.ForRequestedType<IClassA>().TheDefaultIsConcreteType<ClassA>();
            ...etc
        });

但是对于单元测试

        ObjectFactory.Initialize(x =>
        {
            x.ForRequestedType<IClassA>().TheDefaultIsConcreteType<UnitTestClassA>();
            ...etc
        });