单元测试时在方法内部创建模拟对象
本文关键字:创建 模拟 对象 内部 方法 单元测试 | 更新日期: 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
});