2009-06-30 54 views
31

所以我有一个工厂类,我试图找出单元测试应该做什么。从这个question我可以验证返回的接口是我期望的特定具体类型。具有具体类作为返回类型的单元测试工厂方法

我应该检查工厂是否返回具体类型(因为目前没有必要使用接口)?目前我正在做类似如下:

[Test] 
public void CreateSomeClassWithDependencies() 
{ 
    // m_factory is instantiated in the SetUp method 
    var someClass = m_factory.CreateSomeClassWithDependencies(); 

    Assert.IsNotNull(someClass); 
} 

这样做的问题是,Assert.IsNotNull似乎有些多余。

而且,我的工厂方法可能设立特定类的依赖,像这样:

public SomeClass CreateSomeClassWithDependencies() 
{ 
    return new SomeClass(CreateADependency(), CreateAnotherDependency(), 
         CreateAThirdDependency()); 
} 

我想确保我的工厂方法正确设置所有这些依赖。是否没有其他的方法来做到这一点,然后再依赖我的属性,然后在单元测试中检查? (我不是修改测试主题以适应测试的大爱好者)

编辑:为了回应Robert Harvey的问题,我使用NUnit作为我的单元测试框架(但我不会想到它会产生太大的差异)

+0

你使用什么测试框架? – 2009-06-30 03:18:18

+0

一些测试框架要求您的类是虚拟的,以便测试框架可以继承它们。有些不。巨大的差异。 – 2009-06-30 04:15:10

回答

32

通常,创建可用于基于状态的测试的公共属性没有任何问题。是的:它是您创建的用于启用测试场景的代码,但它会伤害您的API吗?可以想象其他客户会在以后找到相同的房产吗?

测试专用代码和测试驱动设计之间有一条细线。我们不应该引入没有其他潜力的代码来满足测试要求,但是引入遵循普遍接受的设计原则的新代码是相当好的。我们让测试驱动我们的设计 - 这就是为什么我们把它称为TDD :)

添加一个或多个属性的一类给用户检查这个类的一个更好的可能性是,在我看来,往往是合理的要做的事情,所以我认为你不应该拒绝引入这些属性。

除此之外,我第二纳德的答案:)

3

我们所做的是与工厂建立依赖关系,当测试运行时,我们使用依赖性注入框架来替代模拟工厂。然后我们对这些模拟工厂设定适当的期望。

3

你可以随时用反射检查东西。没有必要为单元测试暴露一些东西。我觉得我很少需要反思,这可能是一个糟糕的设计的标志。

看着你的示例代码,是的,Assert not null似乎是多余的,取决于你设计工厂的方式,有些会从工厂返回空对象而不是异常。

23

如果工厂正在返回具体的类型,并且您保证您的工厂总是返回一个具体的类型,而不是空的,那么不,在测试中也不会有太多的价值。它确实可以确保,随着时间的推移,这种期望不会被违反,并且不会抛出异常等情况。

这种测试方式只是确保在您未来进行更改时,您的工厂行为在您不知情的情况下不会改变。

如果您的语言支持它,对于您的依赖关系,您可以使用反射。这并不总是最容易维护的,并且将测试与您的实施紧密结合。你必须决定是否可以接受。这种方法往往非常脆弱。

但你真的好像试图分离哪些类被构造,从构造函数的调用方式。使用DI框架来获得这种灵活性可能会更好。当你需要它们时,你不需要给自己多个接缝(一个接缝是一个可以改变你的程序中的行为而不需要在那个地方进行编辑的地方)来处理。

尽管您已经提供了示例,但您可以从工厂派生出一个类。然后覆盖/模拟CreateADependency(),CreateAnotherDependency()CreateAThirdDependency()。现在,当您拨打CreateSomeClassWithDependencies()时,您是否能够意义是否创建了正确的依赖关系。

注意:“seam”的定义来自Michael Feather的书“有效使用遗留代码”。它包含许多技术的例子,以便将测试性添加到未经测试的代码中。你可能会觉得它非常有用。

+1

我喜欢这个答案,如果你不得不处理遗留的或接近传统的(.net 1.1)代码而没有测试,那么这本书就是上帝寄来的。如果你在一个单元测试以前没有完成的团队中,并且你有很多你想得到单元测试的brownfield代码,那么这本书也是非常有用的。 – ElvisLives 2011-01-19 17:51:47

0

据我了解,你要测试的依赖关系是否正确构建并传递到新的实例?

如果我不能使用像谷歌吉斯的框架,我可能会做这样的事情(在这里使用JMock的和Hamcrest):

@Test 
public void CreateSomeClassWithDependencies() 
{ 
    dependencyFactory = context.mock(DependencyFactory.class); 
    classAFactory = context.mock(ClassAFactory.class); 

    myDependency0 = context.mock(MyDependency0.class); 
    myDependency1 = context.mock(MyDependency1.class); 
    myDependency2 = context.mock(MyDependency2.class); 
    myClassA = context.mock(ClassA.class); 

    context.checking(new Expectations(){{ 
     oneOf(dependencyFactory).createDependency0(); will(returnValue(myDependency0)); 
     oneOf(dependencyFactory).createDependency1(); will(returnValue(myDependency1)); 
     oneOf(dependencyFactory).createDependency2(); will(returnValue(myDependency2)); 

     oneOf(classAFactory).createClassA(myDependency0, myDependency1, myDependency2); 
     will(returnValue(myClassA)); 
    }}); 

    builder = new ClassABuilder(dependencyFactory, classAFactory); 

    assertThat(builder.make(), equalTo(myClassA)); 
} 

(如果你不能嘲笑ClassA的,你可以指定一个非模拟版本myClassA使用新的)