假设定义了接口
interface Foo {
int getBaz();
void doBar();
}
进一步假设合同规定每次doBar被称为baz都会增加。 (好的,这是一个人为的代码,但在这里坚持我)
现在我想提供一个单元测试,我可以提供给Foo实施者,以便他们可以验证他们是否符合所有合同条件。
class FooTest {
protected Foo fooImpl;
@Test
public void int testBazIncrement()
{
int b = fooImpl getBaz();
fooImpl.doBar();
Assert.assertEquals( b+1, fooImpl.getBaz();
}
}
将测试提供给Foo的实施者的最佳做法是什么?在我看来,需要一种机制让他们调用FooTest并提供Foo或FooFactory来构造Foo实例。此外,如果有很多测试(想想大接口),那么我想把所有这些测试放在一个FooTest类中。
有没有关于如何实施此类测试的最佳实践?
答案 0 :(得分:2)
这是依赖注入的教科书示例。如果您使用Spring作为DI容器,则可以在fooImpl
@Inject
protected Foo fooImpl;
您的测试需要使用@RunWith(SpringJUnit4ClassRunner.class)
进行注释,并且由接口提供程序来配置Spring及其实现。
如果没有DI容器,您可以创建一个包含所有测试的抽象测试类,以及一个提供实现对象的抽象方法。
protected abstract Foo createFoo();
由实现提供程序来对测试进行子类化并实现抽象方法。
class FooImplTest extends FooTestSuper {
@Override
protected Foo createFoo() {
return new FooImpl();
}
如果您有多个测试,请考虑JUnit的@Suite
注释。它与Spring测试运行器兼容。
答案 1 :(得分:0)
你可以实现一个testDataFactory来实现你的对象,或者使用GSon来创建你的对象(就个人而言,我喜欢GSon,它是清晰而快速的,你可以在几次内学习它)。 对于测试实现,我建议编写更多测试而不是单个测试。 通过这种方式,单元测试可以是独立的,您可以在一个封闭的结构中分离您的问题。
Sonar是一款可以帮助您的工具,可以对您的代码进行分析。您可以从声纳前端看到您的应用程序是如何测试的:
如您所见,Sonar可以向您显示代码的测试位置
答案 2 :(得分:0)
为什么不从一个单元测试中调用 InterfaceTester InterfaceImplATest , InterfaceImplBTest 等?
e.g。
@Test
public void testSerialisation()
{
MyObject a = new MyObject();
...
serialisationTester.testSimpleRoundTrip(a);
serialisationTester.testEdgeCases(a);
...
}
答案 3 :(得分:0)
经过多次思考和一些死胡同后,我开始使用以下模式:
以下内容:
构建接口测试,以便开发人员可以测试实现是否符合合同 在界面和随附文档中列出。
测试中的主要项目使用[INTERFACE] ProducerInterface的实例来创建被测对象的实例。 [INTERFACE] ProducerInterface的实现必须跟踪测试期间创建的所有实例,并在请求时关闭所有实例。有一个Abstract [INTERFACE]生成器可以处理大部分功能,但需要createNewINTERFACE实现。
<强> TESTS 强>
接口测试记为Abstract [INTERFACE]测试。测试通常扩展Abstract [INTERFACE] ProducerUser类。该类处理在测试结束时清理所有图形,并为实现者插入其[INTERFACE] ProducerInterface实现提供了一个钩子。
通常,要实现测试需要几行代码,如下例所示 正在测试新的Foo图实现。
public class FooGraphTest extends AbstractGraphTest {
// the graph producer to use while running
GraphProducerInterface graphProducer = new FooGraphTest.GraphProducer();
@Override
protected GraphProducerInterface getGraphProducer() {
return graphProducer;
}
// the implementation of the graph producer.
public static class GraphProducer extends AbstractGraphProducer {
@Override
protected Graph createNewGraph() {
return new FooGraph();
}
}
}
<强> SUITES 强>
测试套件被命名为Abstract [INTERFACE] Suite。套件包含多个测试,可以测试被测对象组件的所有测试。例如,如果Foo.getBar()返回了Bar接口的实例,则Foo套件包括对Foo iteself的测试以及运行Bar测试Bar。运行套件比运行测试要复杂一些。
使用JUnit 4 @RunWith(Suite.class)和@ Suite.SuiteClasses({})创建套件 注释。这有几个开发人员应该知道的效果:
为了满足这些要求,Abstract [INTERFACE] Suite有一个静态变量,它包含[INTERFACE] ProducerInterface的实例和一些实现“get [INTERFACE] Producer()”的Abstract测试的局部静态实现。方法通过返回静态实例。然后在@ Suite.SuiteClasses批注中使用本地测试的名称。这使得为[INTERFACE]实现创建Abstract [INTERFACE]套件的实例非常简单,如下所述。
public class FooGraphSuite extends AbstractGraphSuite {
@BeforeClass
public static void beforeClass() {
setGraphProducer(new GraphProducer());
}
public static class GraphProducer extends AbstractGraphProducer {
@Override
protected Graph createNewGraph() {
return new FooGraph();
}
}
}
请注意,beforeClass()方法使用@BeforeClass注释。 @BeforeClass使它在类中的任何测试方法之前运行一次。这将设置静态 运行套件之前的图形生成器的实例,以便将其提供给随附的测试。
<强> FUTURE 强> 我希望通过使用java泛型可以实现进一步的简化和删除重复代码,但我还没有达到这一点。
答案 4 :(得分:-1)
以下是关于如何进行合格单元测试的一些想法:
首先,尝试对您的实施者类进行全面测试,这意味着UT应该涵盖所有方法。当您需要重构代码时,这样做可以节省大量时间。对于您的情况,可能是:
class FooTest {
protected Foo fooImpl;
@Test
public void testGetBaz() {
...
}
@Test
public void testDoBar() {
...
}
}
你会发现你需要检查班级的内部状态,并且UT没有任何错误,应该是一种白盒测试。
其次,所有方法都应单独测试,而不是相互依赖。在我看来,对于上面发布的代码,它看起来不仅仅是功能测试或集成测试,但它也是必要的。
第三,我不认为使用spring或其他容器为你组装目标对象是一个好习惯,这将使你的测试运行相对较慢,特别是当有大量测试运行时。你的类本质上应该是pojo,如果目标对象非常复杂,你可以在测试类中用另一种方法完成汇编。
第四,如果某个类的父接口非常大,那么应该将测试方法分成几个组。 Here是更多信息。