0

How do I create an object in a Guice Test module that is used as a mock in one test, but requires to be real object in another.

eg. Consider I have a class called ConfigService. This is injected into another class called UserService using constructor injection. During testing, I use a TestModule which has various classes and their mocks.

TestModule.java:

public class TestModule extends AbstractModule{
    @Override
    public void configure() {
            ConfigService configService = Mockito.mock(ConfigService.class);
            bind(ConfigService.class).toInstance(configService);

            UserService userService = new UserService(configService);

            bind(UserService.class).toInstance(userService);

    }
}

In UserServiceTest, I create an injector and use the instances from this TestModule.

Injector injector = Guice.createInjector(new TestModule());
userService = injector.getInstance(UserService.class);
configService = injector.getInstance(ConfigService.class);

This works fine, the place where I face a problem now is when I need to test ConfigService.class.

If I want to use the same TestModule for the ConfigServiceTest, how do I now change the mock object of ConfigService I created earlier to an actual one for testing. The vice versa is also a problem -> ie. if I have a real object of ConfigService, how do I stub and mock the responses in UserService.class.

Is there a way to achieve this or should I be creating separate test modules for mocks and real objects? Or am I going about the entire process in a wrong way?

leoOrion
  • 1,833
  • 2
  • 26
  • 52
  • Would keeping the modules smaller scope solve the problem? i.e., one module for the `ConfigService` and one for the `UserService`? – David Rawson Mar 13 '18 at 08:52
  • Add mockito runner annotation to the test class @RunWith(MockitoJUnitRunner.class) – Victor Gubin Mar 13 '18 at 09:00
  • @DavidRawson Is there no way to achieve this with the same test module? Do I have to create a scoped test module for every class that comes under this case?. I would assume that the case where an object requires to be a mock in one test but real in another is quite often. How is this normally solved? – leoOrion Mar 13 '18 at 09:15
  • @VictorGubin I don't see how that would help in any way. I don't have a problem with running the tests. – leoOrion Mar 13 '18 at 09:17
  • @DavidRawson it was the first hint. Another hint is spy the Injector and mock only injector.getInstance(ConfigService.class) inside test method. i.e. doReturn( mockConfig ).when(injector).getInstance(ConfigService.class); – Victor Gubin Mar 13 '18 at 09:27
  • So have the real object instantiated in the TestModule. When I need a mock, create a spy on the injector that will use the test module and stub a mock for the object on question... Correct? – leoOrion Mar 13 '18 at 09:31
  • Can you not avoid injecting altogether in the test? I.e., just instantiate the objects using the `new` keyword? – David Rawson Mar 13 '18 at 09:39

1 Answers1

2

You can do that using spy method.

ConfigService realConfigService = new ConfigService();
ConfigService configService = Mockito.spy(realConfigService);
bind(ConfigService.class).toInstance(configService);

What spy does is, whenever you provide stubbing, it will behave as if the object is mocked. Otherwise, it will call the real method of the object.

Please check this answer for more in-depth theory.

Yogesh Badke
  • 4,249
  • 2
  • 15
  • 23