【问题标题】:Make and reference stub object for testing, without needing the class that the stub would extend制作和引用存根对象以进行测试,而不需要存根将扩展的类
【发布时间】:2014-10-26 21:15:30
【问题描述】:

这可能是一个简单的问题,但我无法缩短它。

我正在测试我的一个类,ClassToTest。在生产中,它将对第三方库对象(ThirdPartyClass 的实例)执行操作。

我想用一个存根来模仿那个类。 (使用Martin Fowler's essay 使用的存根定义:存根对测试期间的调用提供预设答案,通常根本不响应测试中编程之外的任何内容

问题是:

  • ClassToTest 有一个方法 setThirdPartyClass(ThirdPartyClass o)。所以要将存根传递给它,存根必须扩展ThirdPartyClass。这意味着我的测试必须使用第三方库,而我的单元测试需要自包含。

原因是,我的代码将在嵌入式系统上运行,而有问题的库主要执行本机代码。但是我想在我的 PC 上单独开发和测试我的代码。但是我不想修改ClassUnderTest参数签名等等,比如来回切换:

classToTest.setThirdPartyClass(ThirdPartyClass o){ /*....*/ }

classToTest.setThirdPartyClass(MyThirdPartyClassStub o){ /*....*/ }

AFAIK,通常存根会简单地扩展它所模仿的类,因此不必更改签名(并用最少的代码覆盖构造函数等)。

这样做的好方法是什么?

【问题讨论】:

  • 你能提供一个你想测试的方法的例子吗?
  • 改用 Mockito 模拟。它使用字节码生成来处理这些问题,并且您将获得一个可编写脚本的替代测试。

标签: java unit-testing junit stub


【解决方案1】:

您的ThridPartyClass 是否实现了接口?如果是这样,您可以更改被测类以使用接口而不是具体类。然后你的存根就实现了接口。

如果没有,有 EasyMockMockito 等模拟库可以为您模拟类。

以easymock为例:

@Test
public void test(){
   ThirdPartyClass mock = createMock(ThirdPartyClass.class);

   expect(mock.foo().andReturn("bar");
   replay(mock);

   classToTest.setThirdPartyClass(mock);
   //perform your tests on classToTest

}

【讨论】:

  • 谢谢 - 不幸的是它没有实现接口;此外,测试将测试被测对象的结果状态,而不是调用对象进行 - 我希望测试独立于我的代码实现。 AFAIK,模拟只能测试对象所做的调用?
  • 模拟第三方类允许您的对象调用该类,我假设该类协同更改您要测试的对象的状态。如果您对间接输出不感兴趣,则无需验证对模拟的调用。
【解决方案2】:

可能不是最好的例子,但本质上是使用 Mockito 来模拟 ThirdPartyClass。

YourClass 进行测试

public class YourClass {

    private String aString;

    // Method that depends on third party component
    public void someMethod(ThirdPartyClass doc) {
        aString = doc.someStringValue();
    }

    public String getValue() {
        return aString;
    }
}

使用 Mockito 模拟 ThirdPartyClass 并控制返回值进行测试

public class YourClassTest {    

    //Class you want to test
    private YourClass testee;

    @Test
    public void testSomeMethod() {

        testee = new YourClass(); 

        //
        // ThirdPartyClass. When the method someStringValue()
        // is called you control the value returned
        //
        ThirdPartyClass testDoc = mock(ThirdPartyClass.class);          
        when(testDoc.someStringValue()).thenReturn("aString");

        // Call method to test and pass mock as argument
        testee.someMethod(testDoc);

        // Check state of your object you want to test
        assertThat(testee.getValue(), equalTo("aString"));      
    }
}

【讨论】:

    【解决方案3】:

    如前所述,模拟(存根)ThirdPartyClass 的最简单方法是该类实现一个接口,并且该接口是您的ClassToTest 使用的完整接口。然后你可以写一个MockThirdPartyimplements ThirdPartyInterface。但我想你不能那样做。

    另一种选择是将ThirdPartyClass 隐藏在您编写的ThirdPartyFacade 类后面。在您编写它时,您可以让它实现您设计的ThirdPartyInterfaceThirdPartyFacade 类的方法只是简单地委托给ThirdPartyClass,因此您可以安全地对该类进行相对较少的测试;不需要单元测试。更改您的ClassToTest,使其使用对ThirdPartyInterface 对象的引用,而不是对ThirdPartyClass 对象的引用。

    您现在可以编写一个用于测试的MockThirdPartyFacade 类,其中implements ThirdPartyInterface 用于测试ClassToTest。您可以根据测试需要使这个模拟类变得简单或复杂。不同的测试用例甚至可以使用不同的模拟类;你不需要一个通用的模拟类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-18
      • 2015-01-26
      • 1970-01-01
      相关资源
      最近更新 更多