I’m trying to test my controller method in Play framework 2.4.6. Inside my controller method, I have the following code:
User user = accountService.getUserByEmail(email); if (user == null) { //Path A } //Path B
When running the test, user
will be null. Hence I can’t test Path B
. I tried returning a User using Mockito when
, but it didn’t work either. Is there any other way of doing it?
Below is my test code:
RequestBuilder request = new RequestBuilder() .method("POST") .bodyForm(ImmutableMap.of("email", "[email protected]")) .uri(controllers.routes.ResetPasswordController.postResetPassword().url()); when(accountService.getUserByEmail(anyString())).thenReturn(new User()); assertEquals(OK, route(request).status());
Answer
Thanks to @Andriy for pointing me in the right direction for Dependency Injection.
I managed to solved the issue with the following setup.
Test:
public class TestClass { @Inject Application application; final AccountService accountServiceMock = mock(AccountService.class); @Before public void setup() { Module testModule = new AbstractModule() { @Override public void configure() { bind(AccountService.class).toInstance(accountServiceMock); } }; GuiceApplicationBuilder builder = new GuiceApplicationLoader() .builder(new ApplicationLoader.Context(Environment.simple())) .overrides(testModule); Guice.createInjector(builder.applicationModule()).injectMembers(this); Helpers.start(application); } @Test public void testMethod() throws Exception { RequestBuilder request = new RequestBuilder() .session("userId", "1") .uri(controllers.routes.AccountController.addAccount().url()); running(application, () -> { when(accountServiceMock.addAccount().thenReturn(true); assertEquals(OK, route(request).status()); }); }
Controller:
@Singleton public class AccountController extends Controller { private AccountService accountService; @Inject public Controller(AccountService a) { accountService = a; } public Result addAccount() { boolean success = accountService.addAccount(); } }
Interface:
@ImplementedBy(AccountServiceImpl.class) public interface AccountService { boolean addAccount(); }
Implementation:
public class AccountServiceImpl implements AccountService { @Override public boolean addAccount() { } }
My knowledge is minimal on the concepts going on here, but roughly:
- Controller is stateless just like HTML, hence you need runtime dependency injection to get Play to recognise the mock object.
Useful documentations: https://www.playframework.com/documentation/2.4.x/JavaTestingWithGuice https://www.playframework.com/documentation/2.4.x/JavaDependencyInjection