简体   繁体   中英

Mocking a spring class with constructor injection

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?

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.

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