Java 生成并引用存根对象进行测试,而不需要存根将扩展的类

Java 生成并引用存根对象进行测试,而不需要存根将扩展的类,java,unit-testing,junit,stub,Java,Unit Testing,Junit,Stub,这可能是一个简单的问题,但我没能缩短它 我正在测试我的一个类,ClassToTest。在生产环境中,它将对第三方库对象执行操作,例如ThirdPartyClass 我想用一个存根来模拟那个类。(使用所使用的存根定义:存根为测试期间发出的呼叫提供固定应答,通常对测试中编程的内容以外的任何内容都不响应) 问题是: ClassToTest有一个方法setThirdPartyClass(ThirdPartyClass o)。因此,要将存根传递给它,存根必须扩展ThirdPartyClass。这意味着我

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

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

我想用一个存根来模拟那个类。(使用所使用的存根定义:存根为测试期间发出的呼叫提供固定应答,通常对测试中编程的内容以外的任何内容都不响应)

问题是:

  • ClassToTest
    有一个方法
    setThirdPartyClass(ThirdPartyClass o)
    。因此,要将存根传递给它,存根必须扩展
    ThirdPartyClass
    。这意味着我的测试必须使用第三方库,而我的单元测试需要是自包含的
原因是,我的代码将在嵌入式系统上运行,所讨论的库主要执行本机代码。但是我想在我的电脑上单独开发和测试我的代码。但我不想修改
ClassUnderTest
参数签名等等,例如,在以下两种情况之间来回切换:

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

好的,通常存根只是扩展它所模仿的类,所以签名不必更改(并用最少的代码重写构造函数等)


这样做的好方法是什么?

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

如果没有,则可以使用诸如和之类的模拟库为您模拟该类

例如,使用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

}

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

如果没有,则可以使用诸如和之类的模拟库为您模拟该类

例如,使用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

}

可能不是最好的例子,但本质上,使用Mockito来模拟第三方类

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"));      
    }
}

可能不是最好的例子,但本质上,使用Mockito来模拟第三方类

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"));      
    }
}

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

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


现在可以编写一个用于测试的
MockThirdPartyFacade
类,该类
实现第三方接口
,用于测试
ClassToTest
。您可以根据测试的需要使这个模拟类变得简单或复杂。不同的测试用例甚至可以使用不同的模拟类;您不需要一个通用的模拟类。

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

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


现在可以编写一个用于测试的
MockThirdPartyFacade
类,该类
实现第三方接口
,用于测试
ClassToTest
。您可以根据测试的需要使这个模拟类变得简单或复杂。不同的测试用例甚至可以使用不同的模拟类;您不需要通用的模拟类。

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