Test a ServiceA and replace all calls to ServiceB with a MockServiceB

Viewed 35

Is it possible to test methods of serviceA and tell the test environment to replace all calls from serviceA to serviceB with a mockServiceB?

This is the method I´d like to test (ServiceA.java):

   
    @Inject
    ServiceB serviceB;

    public boolean tokenExists(ItemModel item) throws Exception {
        try {
            List<ItemModel > items = serviceB.getItems();
            String token = item.getToken();
            return items.stream().filter(i -> i.getToken().equals(token)).findFirst().isPresent();
        } catch (ApiException e) {
            throw new Exception(500, "Data could not be fetched.", e);
        }
    }

Since serviceB.getItems() will result in a REST-call, I'd like to replace calls to serviceB with a custom mockServiceB where I just load mock data from a json file like this (TestServiceA.java):

    @InjectMock
    ServiceB serviceB;

    @ApplicationScoped
    public static class ServiceB {

        private ObjectMapper objMapper = new ObjectMapper();;

        public List<ItemModel> getItems() throws IOException {
            final String itemsAsJson;

            try {
                itemsAsJson= IOUtils.toString(new FileReader(RESOURCE_PATH + "items.json"));
                objMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                List<Item> items= objMapper.readValue(itemsAsJson, objMapper.getTypeFactory().constructCollectionType(List.class, ItemModel.class));
                return items;
            } catch (IOException e) {
                throw e;
            }
        }
    }

And then test the tokenExists method like this (TestServiceA.java):

    @Test
    public void testTokenExists() {
        try {
            Assertions.assertTrue(this.serviceA.tokenExists(this.getTestItem()));
        } catch (Exception e) {
            fail("exception");
        }
    }

However, when I run the test testTokenExists it still calls the original serviceB.getItems(). So my question would be if this is even possible or if I have to take a different approach.

1 Answers

You need to mock the serviceB inside ServiceA class, you have two different options:

First You can use whitebox or reflection and assign the mocked the serviceB to ServiceA class

public class TestExample {
    @Mock
    private ServiceB mockedServiceB;
    private ServiceA serviceA;

    @Before
    public void setUp() {
       serviceA = new ServiceA();
    }

    @Test
    public void shouldDoSomething() {
        // Arrange
        Whitebox.setInternalState(serviceA, "serviceB", mockedServiceB);
        when(serviceB.getItems()).thenReturn(list);

       // Act & Assert
       Assertions.assertTrue(serviceA.tokenExists(this.getTestItem()));
    }
}

Second

You can use @InjectMocks

public class TestExample {
    @Mock
    private ServiceB mockedServiceB;
    @InjectMocks
    private ServiceA serviceA;

    @Before
    public void setUp() {
       serviceA = new ServiceA();
    }

    @Test
    public void shouldDoSomething() {
       // Arrange
        List<Item> list = new ArrayList<>();
        list.add(new Item(...)); // add whatever you want to the list
        when(serviceB.getItems()).thenReturn(list);
        
       // Act & Assert
       Assertions.assertTrue(serviceA.tokenExists(this.getTestItem()));
    }
}

Note: prepare your runner also

Related