Mocking a spring class with constructor injection

Morfildur :

I have the following class:

@Service
public class SomeClass extends BaseClass {
  private final SomeService someService;

  public SomeClass(SomeService someService) {
    this.someService = someService;
  }

  public List<String> methodToTest(SomeDataClass data) {
    if (!checkData(data)) { // checkData is defined in BaseClass and does lots of unimportant stuff
      throw BadDataException();
    }

    return someService.getData();
  }
}

I now want to create testcases for SomeClass using Mockito.

  1. Test that checkData() is called.
  2. Test that someService.getData() is called.

Using Mockito, I've managed testcase 1 with:

@Test(expected = BadDataException.class)
public void methodToTest_checkDataCalled() {
  SomeClass mock = mock(SomeClass.class);
  when(mock.methodToTest(any()).thenCallRealMethod();
  when(mock.checkData(any()).thenReturn(false);

  mock.methodToTest(new SomeData());
  verify(mock).checkData(any());
}

However, I have problems creating testcase #2. I can either create SomeClass manually with SomeClass someClass = new SomeClass(mockSomeService), but then I need to create mock data for the extensive checkData() request, which I don't actually want to test in this testcase since it's already covered by a different test and which requires a lot of unnecessary mocking, or I can mock SomeClass like in testcase #1, but then someService is always null, no matter what I try.

I've tried variations of @InjectMocks, @Mock and such, but I've failed to create a workable solution.

Things I've tried:

@Mock
private SomeService someService;

@Before
public void setUp() {
  someService = mock(SomeService.class);
  when(someService.getData()).thenReturn(Collections.singletonList("Mock"));
}

@Test
public void methodToTest_someServiceCalled1() {
  SomeClass mock = mock(SomeClass.class);
  when(mock.methodToTest(any()).thenCallRealMethod();
  when(mock.checkData(any()).thenReturn(true);

  List<String> result = mock.methodToTest(new SomeData()); // NullPointerException at someService.getData() call
  verify(someService).getData();
  assertEquals("Mock", result.get(0));
}

@Test
public void methodToTest_someServiceCalled2() {
  SomeClass someClass = new SomeClass(someService);
  List<String> result = someClass.methodToTest(new SomeData()); // Fails at checkData() call
  verify(someService).getData();
  assertEquals("Mock", result.get(0));
}

How can I have a call a mock for someService while still mocking the call to the parent classes checkData()? Is it possible without refactoring SomeClass?

second :

If you create a mock of a class there won't be any dependencies in it. Instead use a spy on a real instance.

Here is a working example.

@Mock
private SomeService someService;

@InjectMocks
private SomeClass classUnderTest;

@Before
public void setUp() {
    Mockito.when(someService.getData()).thenReturn(Collections.singletonList("Mock"));
}

@Test
public void methodToTest_someServiceCalled1() {

  SomeClass spy = Mockito.spy(classUnderTest);
  Mockito.when(spy.checkData(Mockito.any())).thenReturn(true);

  List<String> result = spy.methodToTest(new SomeData());
  Mockito.verify(someService).getData();
  assertEquals("Mock", result.get(0));
}

I am not sure about you're 2nd test, afaik I would call the original checkData method. As you mentioned that is something you want to avoid, the solution should be similiar to the 1st test.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=361468&siteId=1