Test class with a new() call in it with Mockito
For the future I would recommend Eran Harel's answer (refactoring moving new
to factory that can be mocked). But if you don't want to change the original source code, use very handy and unique feature: spies. From the documentation:
You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).
Real spies should be used carefully and occasionally, for example when dealing with legacy code.
In your case you should write:
TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);
I am all for Eran Harel's solution and in cases where it isn't possible, Tomasz Nurkiewicz's suggestion for spying is excellent. However, it's worth noting that there are situations where neither would apply. E.g. if the login
method was a bit "beefier":
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
... and this was old code that could not be refactored to extract the initialization of a new LoginContext
to its own method and apply one of the aforementioned solutions.
For completeness' sake, it's worth mentioning a third technique - using PowerMock to inject the mock object when the new
operator is called. PowerMock isn't a silver bullet, though. It works by applying byte-code manipulation on the classes it mocks, which could be dodgy practice if the tested classes employ byte code manipulation or reflection and at least from my personal experience, has been known to introduce a performance hit to the test. Then again, if there are no other options, the only option must be the good option:
@RunWith(PowerMockRunner.class)
@PrepareForTest(TestedClass.class)
public class TestedClassTest {
@Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
TestedClass tc = new TestedClass();
tc.login ("something", "something else");
// test the login's logic
}
}