Jayy
Jayy

Reputation: 14728

Make and reference stub object for testing, without needing the class that the stub would extend

This is probably a simple question, I haven't been able to shorten it though.

I am testing a class of mine, ClassToTest. In production, it will perform operations on a third party library object, instance of ThirdPartyClass.

I want to mimic that class with a stub. (Using the definition of stub as used by Martin Fowler's essay: Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test)

The problem is:

The reason is, my code will be run on an embedded system, and the library in question mostly executes native code. But I want to develop and test my code in isolation on my PC. But I don't want to have to modify ClassUnderTest argument signatures and so on, for example, changing back and forth between:

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

and

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

AFAIK, normally a stub would simply extend the class it's mimicking, so the signatures would not have to be changed (and overriding constructors etc with minimal code).

What is a good way of doing this?

Upvotes: 2

Views: 2138

Answers (3)

clD
clD

Reputation: 2611

Possibly not the best example but essentially, using Mockito to mock the ThirdPartyClass.

YourClass to test

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

Test using Mockito to mock the ThirdPartyClass and control the value returned

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

Upvotes: 2

Raedwald
Raedwald

Reputation: 48644

The easiest way of mocking (stubbing) ThirdPartyClass, as has been mentioned, would be if that class implements an interface, and that interface is the complete interface your ClassToTest uses. Then you could write a MockThirdParty class that implements ThirdPartyInterface. But I guess you can not do that.

Another option is to hide the ThirdPartyClass behind a ThirdPartyFacade class that you write. As you write it, you can have the it implement a ThirdPartyInterface that you design. The methods of the ThirdPartyFacade class simply delegate to the ThirdPartyClass, so it is safe for you to do relatively little testing of that class; unit tests are not necessary for it. Alter your ClassToTest so it uses a reference to a ThirdPartyInterface object, rather than a reference to a ThirdPartyClass object.

You can now write a MockThirdPartyFacade class for testing, which implements ThirdPartyInterface, for testing ClassToTest. You can make this mock class as simple or as complicated as you tests need it to be. Different test cases could even use different mock classes; you do not need a general purpose mock class.

Upvotes: 2

dkatzel
dkatzel

Reputation: 31648

Does your ThridPartyClass implement an interface? If so you can change your class under test to use the interface instead of the concrete class. Then your stub just implements the interface.

If not, there are mocking libraries such as EasyMock and Mockito which can mock the class for you.

Using easymock for example:

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

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

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

}

Upvotes: 2

Related Questions