简体   繁体   中英

Mocking internal instantiated object

I'm writing a test class for testing my 'ImporterService' class. This service reads an InputStream and creates an Object from it's data. The Object, in this case a Builder class, is instantiated within the 'ImporterService' class. To test my 'ImporterService' class I need to verify the invocations on the Builder class. For this I want to use a Mocking framework, however how is it possible to create a mock instance of the 'Builder' object outside the 'ImporterService'?

The method of my 'ImporterService' class looks like:



    public Builder importFrom(BufferedReader reader) throws IOException {
        String someValue = readFrom(reader);
        Builder builder = new Builder();   // I need to mock this Builder object...
        builder.someMethod(someValue);     // to see of a method is called with the expected value
    }

I was thinking about moving the creation of the Builder class into a protected method which I can override on setup of the test. But this solution seems not very nice to me as the 'ImporterService' class is leaking some internal logic and makes it possible to override the method by other classes which I do not want.

If you use any dependency injection library (like Spring) you could inject mock object instead of builder to ImporterService class. Or you could replace call to constructor with call to factory and use factory, that returns mocks in test code.

Yes, you can either do as you suggested or :

Create a Factory class in which you create Builder objects and assign it to the reader class. In your unit tests, mock this factory and force it to build a Builder of your choice that you can check for method calls in your unit test.

Here is an example using EasyMock showing how you can achieve this:

public class Reader{
   private BuilderFactory factory = new BuilderFactory(); // Use production factory by default
   public Builder importFrom(BufferedReader reader) throws IOException {
        String someValue = readFrom(reader);
        Builder builder = factory.buildBuilder();
        builder.someMethod(someValue);     // to see of a method is called with the expected value
    }
}

In your unit tests you do the following:

Reader classUnderTest = new Reader();
BuilderFactory fakeFactory = EasyMock.createNiceMock(BuilderFactory.class);
Builder builder = EasyMock.createMock(Builder.class);
EasyMock.expect(fakeFactory.buildBuilder()).andReturn(builder);
builder.someMethod("value here");
EasyMock.expectLastCall().once();
EasyMock.replay(fakeFactory, builder);
classUnderTest.importFrom(bufferReader);
// Very that all calls were correctly performed on the builder
EasyMock.verify(builder);

Assuming you are using jMockt ( I recomment this mocking framework ) , you will be able to do following:

  @Test
    public void testFoo(@Mocked Builder builder) {
        new Expectations() {
            {
                new Builder();
                returns(builder);

                builder.setSomemethod()
                ...
            }
        };

        assertSame(builder,impoertesService.importFrom(...));
    }

一些模拟框架(如PowerMock)可以模拟对象的构造。

you can change function signature to something like this:

public Builder importFrom(BufferedReader reader, Builder builder) throws IOException {

by doing this you can pass any dummy implementation from your test case. This is one way of dependency injection.

The idea of dependency injection is to ask for all your dependencies and if a class/function does that, the code becomes highly testable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM