我有以下代码,我正在尝试对其进行单元测试:
public override IRenderable GetRenderable()
{
var val = SomeCalculationUsingClassMemberVariables();
return new EquationRenderable(val);
}
好像我想在这里使用工厂,所以我可以将IRenderable的创建与这个类分开。问题是我有许多这些类创建了以不同方式构造的不同IRenderables,因此我需要为每个类实现一个新的工厂方法。解决这个问题的最佳方法是什么?
答案 0 :(得分:1)
取决于具体IRenderable构造函数的一致性,您可以使用以下模式进行工厂创建
public IRenderable CreateInstance<T>(object calculation) where T : IRenderable
{
Activator.CreateInstance<T>(new[] { calculation });
}
或者如果你有许多不同的构造函数,你可以使用params关键字来传递任意数量的参数
public IRenderable CreateInstance<T>(params object[] args) where T : IRenderable
{
Activator.CreateInstance<T>(args);
}
为了能够在调用Activator.CreateInstance之前对您使用此代码的参数进行某种运行时检查
var types = args.Select(o => o.GetType()).ToArray();
var c = typeof(T).GetConstructor(types);
if (c == null)
{
throw new InvalidOperationException("No matched constructor")
}
答案 1 :(得分:1)
好问题。
首先,由于DI容器没有以“正确”的方式使用或者设计可以改进,因此感觉诱惑使用AbstractFactories到处都有点闻。
但有时候我也会遇到这个问题。我看到以下内容:
注射已经提到了,所以我不再重复了。我更喜欢Java,所以请原谅一些语法错误。
如果'new'创建的实例是域对象而不是服务,我经常使用它。因为它是直接在方法中返回的,所以可以用我的测试来测试直接输出。
Prod-Code:
...
public override IRenderable GetRenderable()
{
var val = SomeCalculationUsingClassMemberVariables();
return new EquationRenderable(val);
}
Test Case:
...
[Test]
public void test_new()
{
SUT sut = ...;
IRenderable r = sut.GetRenderable();
assertTrue(r instanceof EquationRenderable);
}
只有在以某种方式将其作为返回值时,才能测试上面的直接输出。如果代码的“副作用”是间接输出,那么事情变得更加复杂,你无法直接通过返回值来感知。如果是这样,我经常提取新创作的方法,然后在我的测试中控制它。这很令人讨厌,我更多地使用它来安全地进行测试,然后开始更多的重构(DI和工厂)。我有时会在遗留代码中执行此操作,其中直接使用“new”创建服务,并且在没有测试的情况下对DI进行重构风险太大。
Prod-Code:
...
public override IRenderable GetRenderable()
{
var val = SomeCalculationUsingClassMemberVariables();
return createEquationRenderable();
}
public IRenderable createEquationRenderable()
{
return new EquationRenderable(val);
}
Test Case:
...
class Stubbed : SUT
{
boolean called = false;
public override EquationRenderable createEquationRenderable()
{
called=true;
return MyMock();
}
}
[Test]
public void test_new()
{
Stubbed sut = new Stubbed();
sut.GetRenderable();
assertTrue(sut.called);
// do further stuff on MyMock
}
我知道,这个例子有点矫枉过正,有点无意义,只是为了描述这个想法。我相信上面可以用C#的模拟框架来缩短。无论如何,测试返回值直接输出更简单,更方便。
也许你有一个更详细的例子?
答案 2 :(得分:0)
更好的方法可能是简单地对代码进行单元测试,而不是重构代码。从技术上讲,这可以通过使用合适的模拟工具来完成,例如TypeMock Isolator或Microsoft Moles(还有第三个我现在不记得了)。