I have just started with testing and I'm also using MVP pattern in my android apps.
Currently I have a presenter which uses the following android code to test if an email address is valid :
android.util.Patterns.EMAIL_ADDRESS.matcher(target).matches()
Since it's not possible to test it with a local JVM unit test I decided to hide it behind an interface :
public interface EmailValidator {
boolean isValidEmail(CharSequence target);
}
and here's the implementation :
public class EmailValidatorImpl implements EmailValidator {
#Override
public boolean isValidEmail(CharSequence target) {
if (target == null) {
return false;
} else {
return android.util.Patterns.EMAIL_ADDRESS.matcher(target).matches();
}
}
}
So now my test code is :
public class SignUpPresenterTest {
#Mock
private SignUpMVP.View view;
#Mock
private EmailValidator validator;
private SignUpPresenter presenter;
private String email = "name#gmail.com";
private String password = "ABCDabcd";
private String username = "username";
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
presenter = new SignUpPresenter(view);
}
#Test
public void onButtonSignUpClicked() throws Exception {
when(validator.isValidEmail(email))
.thenReturn(true);
presenter.onButtonSignUpClicked(email, password, username);
verify(view).executeSignUpService();
}
}
and now I'm getting a NPE when the code above calls : EmailValidatorImpl.isValidEmail()
java.lang.NullPointerException
at com.example.signup.helpers.EmailValidatorImpl.isValidEmail(EmailValidatorImpl.java:20)
at com.example.signup.SignUpPresenter.showErrors(SignUpPresenter.java:67)
at com.example.signup.SignUpPresenter.onButtonSignUpClicked(SignUpPresenter.java:25)
at com.example.signup.SignUpPresenterTest.onButtonSignUpClicked(SignUpPresenterTest.java:43)
and my questions are : isn't this how I Should use Mockito ? Is there anything I can do to test my code and avoid NPE ?
That happens because you are mocking the EmailValidator but you are not using that object inside SignUpPresenter.
You should pass the reference in the constructor:
presenter = new SignUpPresenter(view, validator);
or in the method signature:
presenter.onButtonSignUpClicked(email, password, username, validator);
Then you presenter should be something like this:
public class SignUpPresenter {
private View view;
private EmailValidator validator;
public SignUpPresenter(View view, EmailValidator validator) {
this.view = view;
this.validator = validator;
}
private void onButtonSignUpClicked(String email, String password, String username) {
//Your code...
boolean isValid = validator.isValidEmail(email);
}
}
You should try with this, because you need to provide a real implementation of your interface
#InjectMocks
private EmailValidator validator = new EmailValidatorImpl();
Related
Hi I have this simple code for my Spring Boot Project:
#Component
public class UserRowMapper implements RowMapper<User> {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(id))
.userName(rs.getString(userName)).build();
}
}
what I want is to create a simple Mockito Test that will check #Value strings like so:
#ExtendWith(MockitoExtension.class)
class UserRowMapperTest {
#Mock
Environment environment;
#Mock
ResultSet resultSet;
#InjectMocks
UserRowMapper userRowMapper;
#Test
void testMapRow() {
when(environment.getProperty("user.id")).thenReturn("id");
when(environment.getProperty("user.userName")).thenReturn("userName");
try {
final User user = userRowMapper.mapRow(resultSet, anyInt());
// check if its ok
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
But I can't find a simple way to check if the value I injected is what I expect.
any ideas?
Unfortunately, there is no mocking mechanism for Spring's #Value. However, you can use a simple workaround using ReflectionUtils that serves for this purpose according to the JavaDoc:
ReflectionTestUtils is a collection of reflection-based utility methods for use in unit and integration testing scenarios.
There are often times when it would be beneficial to be able to set a non-public field, invoke a non-public setter method, or invoke a non-public configuration or lifecycle callback method when testing code involving
ReflectionTestUtils.setField(userRowMapper, "id", "my-id-value");
ReflectionTestUtils.setField(userRowMapper, "userName", "my-userName-value");
JavaDoc for ReflectionTestUtils#setField(Object, String, Object).
Add getter methods for id and userName fields instead of mocking Environment class.
#Component
public class UserRowMapper implements RowMapper<User> {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(getId()))
.userName(rs.getString(getUserName())).build();
}
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
While mocking:
Mockito.when(userRowMapper.getId()).thenReturn("id");
Mockito.when(userRowMapper.getUserName()).thenReturn("userName");
Also, you can use TestPropertySource annotation to provide altogether different properties file:
#SpringBootTest
#TestPropertySource(locations = "/application2.properties")
public class TestClassTest {
#Autowired
TestClass testClass;
#Test
public void test() {
assertEquals("id", testClass.getId());
}
}
I would rather suggest to you to do not use inline #Value annotation on the consumer class. As you have seen, the class testability decreases.
You can solve your problem simply creating a #Configuration bean and injecting it to the UserRowMapper class. In this way, using DI you can easily mock the configuration in your tests.
See below a naïve implementation.
#Configuration
public class UserRowMapperConfiguration {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
#Component
public class UserRowMapper implements RowMapper<User> {
private UserRowMapperConfiguration configuration;
public UserRowMapper (UserRowMapperConfiguration configuration) {
this.configuration = configuration;
}
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(this.configuration.getId()))
.userName(rs.getString(this.configuration.getUserName())).build();
}
}
#ExtendWith(MockitoExtension.class)
class UserRowMapperTest {
#Mock
UserRowMapperConfiguration configuration;
#Mock
ResultSet resultSet;
#InjectMocks
UserRowMapper userRowMapper;
#Test
void testMapRow() {
when(configuration.getId()).thenReturn("id");
when(configuration.getUserName()).thenReturn("userName");
try {
final User user = userRowMapper.mapRow(resultSet, anyInt());
// check if its ok
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
As thepaoloboi suggested use a configuration class to hold all your configs.
Now to test that your config is pointing to the right #Value key, you create an integration test by simply loading that object using spring without loading the whole context. That way it'll be as fast as a unit test.
Here's an example:
#ExtendWith(SpringExtension.class)
#Import(UserRowMapperConfiguration.class)
#TestPropertySource(properties = { "user.id=id" , "user.userName=userName"})
class UserRowMapperConfigurationTest {
#Autowired
UserRowMapperConfiguration userRowMapperConfiguration;
#Test
void test() {
assertEquals("id",userRowMapperConfiguration.getId());
assertEquals("userName",userRowMapperConfiguration.getUserName());
}
}
and Configuration class:
#Configuration
public class UserRowMapperConfiguration {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
I have following service
public UserActivityLog save(UserActivityLog userActivityLog){
LOGGER.debug("User activity save called.");
userActivityLog.setCreatedByUser(User.getUser().getUserId());
userActivityLog.setCreationTime(new Date());
return activityLogRepository.save(userActivityLog);
}
I had written following Junit for test this but getting null pointer for User
#RunWith(MockitoJUnitRunner.class)
public class ActivityHistoryServiceTest {
#InjectMocks
private ActivityHistoryService activityHistoryService;
#Mock
private ActivityLogRepository activityLogRepository;
#Test
public void testSave() {
UserActivityLog userActivityLog = new UserActivityLog();
/* Called some setter methods to set value here*/
UserProfile profile = new UserProfile();
Mockito.when(User.getUser()).thenReturn(profile);
Assert.assertNotNull(activityLogRepository.save(userActivityLog));
}
}
User class is like
public class User {
private User(){}
public static UserProfile getUser(){
/*some logic*/
return userProfile;
}
}
Please help for this. thanks in advance.
Static methods are useful when they are useful. Use them as a replace for singletons is not a very good practice. Instead of what you have your code could look like this:
public class MyClass {
private final Logger logger;
private final UserProvider userProvider;
private final DateProvider dateProvider;
/** Constructor that sets all attributes */
public MyClass(...) {...}
public UserActivityLog save(UserActivityLog userActivityLog){
LOGGER.debug("User activity save called.");
userActivityLog.setCreatedByUser(userProvider.get().getUserId());
userActivityLog.setCreationTime(dateProvider.get());
return activityLogRepository.save(userActivityLog);
}
}
Provider classes are quite simple. They just have a get() method that returns an instance of required type:
public class DateProvider {
public Date get() {
return new Date();
}
}
public class UserProvider {
public User get() {
...
}
}
With this setup you can easily mock the dependencies
You need PowerMock to mock the static methods.
Refer:
https://blog.codecentric.de/en/2016/03/junit-testing-using-mockito-powermock/
I'm trying to unit test the code of my Presenter. As you can see below in the code I'm making a Retrofit request and if the response is successful I call a method from the View.
Code of my Presenter I want to test :
#Override
public void onLoadChatrooms(String accountId, String pageNum) {
getChatroomsService.getChatrooms(apiToken, createRequestBodyForGetChatroomsRequest(accountId, pageNum))
.enqueue(new Callback<GetChatroomsServiceResponse>() {
#Override
public void onResponse(Call<GetChatroomsServiceResponse> call, Response<GetChatroomsServiceResponse> response) {
if (response.isSuccessful()) {
view.showData(Arrays.asList(response.body().getChatRoomsArray()));
}
}
#Override
public void onFailure(Call<GetChatroomsServiceResponse> call, Throwable t) {
}
});
}
And here is the test I wrote :
#Mock
private ChatMVP.View view;
#Mock
private GetChatroomsService getChatroomsService;
#Mock
private RequestBody requestBody;
#Mock
private Call<GetChatroomsServiceResponse> call;
#Captor
private ArgumentCaptor<Callback<GetChatroomsServiceResponse>> callback;
#Mock
private List<GetChatroomsResponseNestedItem> chatroomsResponseNestedItems;
private String accountId = "14";
private String apiToken = "someToken";
private ChatPresenter chatPresenter;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
chatPresenter = new ChatPresenter(view, getChatroomsService, apiToken);
}
#Test
public void onLoadChatrooms() throws Exception {
when(getChatroomsService.getChatrooms(apiToken, requestBody))
.thenReturn(call);
chatPresenter.onLoadChatrooms(accountId, "0");
verify(call).enqueue(callback.capture());
callback.getValue().onResponse(call, getResponse());
verify(view).showData(chatroomsResponseNestedItems);
}
The problem is that I'm getting a NPE for line :
chatPresenter.onLoadChatrooms(accountId, "0");
The exact error message is :
java.lang.NullPointerException
at my.package.main.fragments.chat.ChatPresenter.onLoadChatrooms(ChatPresenter.java:40)
at my.package.main.fragments.chat.ChatPresenterTest.onLoadChatrooms(ChatPresenterTest.java:70)
where line 40 for ChatPresenter is : .enqueue(new Callback<GetChatroomsServiceResponse>() {
Anyone can help with that ? I tried checking if Presenter is null and that's not the problem.
EDIT :
ChatPresenter's constructor :
class ChatPresenter implements ChatMVP.Presenter {
private ChatMVP.View view;
private GetChatroomsService getChatroomsService;
private String apiToken;
#Inject
ChatPresenter(ChatMVP.View view, GetChatroomsService getChatroomsService, #Named("Api-Token") String apiToken) {
this.view = view;
this.getChatroomsService = getChatroomsService;
this.apiToken = apiToken;
}
and GetChatroomsService :
interface GetChatroomsService {
#POST("getchatrooms")
Call<GetChatroomsServiceResponse> getChatrooms(#Query("api_token") String apiToken, #Body RequestBody requestBody);
}
The problem here is that the mocked method getChatrooms() in getChatroomsService returns a null. The most likely reason for this is that the parameters given in your production code do not match the parameters in your mock configuration.
I for myself use the any*() matcher when configuring the mocks and verify the parameters passed in by the production code explicitly which saves me from non descriptive NPEs like this.
#Test
public void onLoadChatrooms() throws Exception {
when(getChatroomsService.getChatrooms(anyString(), any(RequestBody.class)))
.thenReturn(call);
chatPresenter.onLoadChatrooms(accountId, "0");
verify(call).enqueue(callback.capture());
callback.getValue().onResponse(call, getResponse());
verify(getChatroomsService).getChatrooms(apiToken,requestBody);
verify(view).showData(chatroomsResponseNestedItems);
}
I have the following test method:
#RunWith(MockitoJUnitRunner.class)
public class AccountManagerTest {
#InjectMocks
private AccountManager accountManager = new AccountManagerImpl(null);
#Mock
private AuthStorage authStorage;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
/* REGISTER TESTS */
#Test
public void test_whenRegister_withAlreadyExistingEmail_thenDoNotRegister() throws AuthStorageException {
String email = "foo#bar.com";
String name = "Foo";
String password = "123456";
String password2 = "123456";
doThrow(new AuthStorageException("Email already in use")).when(authStorage).registerNewUser(Matchers.any());
assertFalse(accountManager.register(email, name, password, password2));
}
}
that tests the following class method:
#Override
public Boolean register(String email, String name, String password, String password2) {
if (password.equals(password2)) {
try {
String pwd = hashPassword(password);
User user = new User(email, name, pwd);
AuthStorage authStorage = new AuthStorageImpl();
authStorage.registerNewUser(user);
return true;
} catch (NoSuchAlgorithmException | AuthStorageException e) {
return false;
}
}
// If passwords don't match
return false;
}
Supposedly, when calling registerNewUser it should thow an exception and then the method would return false, but when debugging I see that the exception isn't thrown and the program returns true. What am I doing wrong?
First of all you shouldn't instantiate the object where mocks are inserted:
#InjectMocks
private AccountManager accountManager = new AccountManagerImpl(null);
Instead, use this:
#InjectMocks
private AccountManager accountManager;
Then if you use Mockito runner:
#RunWith(MockitoJUnitRunner.class)
You shouldn't inject mocks directly:
#Before
public void setup() {
MockitoAnnotations.initMocks(this); //remove this line
}
And the last point: there is no point to your mocking because you have a local variable in your register method:
AuthStorage authStorage = new AuthStorageImpl();
authStorage.registerNewUser(user);
Which makes the class use your mocked object.
Hi I request your help to know how to emulate the method of the class Validator.validateConnection();.
The problem is that the method validateConnection not exist in the class Class_Implementation and I don't want to create that method in the class Class_Implementation. The method validateConnection do a connection to the database to know if the connection is alive. When Mockito runs I get a java.Lang.NullPointerException that is caused by NamingException - need to specify class name in environment.
The real problem is when I call in Mockito test the line:
Boolean resp = mockImpl.checkConnection();
..in the checkConnection() the class Validator.validateConnection(); is trying to connect to database. I just want emulate this line and return true or false, but the problem is that the method validateConnection() is an instance of class Validator.
If need more information for fix this please let me know.
public class Class_Implementation {
public boolean checkConnection() {
boolean isConnectionAlive = false;
Validator.validateConnection();
// another things for do
return false;
}
}
public class Validator {
public static Boolean validateConnection() {
Connection conn = new Connection();
Boolean connectionAlive = false;
connectionAlive = conn.isConnectionAlive();
if (connectionAlive) {
return true;
} else {
return false;
}
}
}
public class Connection {
public boolean isConnectionAlive() {
// Code for connection to DB
}
}
// class for do the test
#RunWith(PowerMockRunner.class)
#PrepareForTest({Class_Implementation.class,Validator.class})
public class TestConnection {
#Test
public void validate_Connection() throws Exception {
Class_Implementation mockImpl = PowerMock.createPartialMock(Class_Implementation.class);
PowerMock.mockStatic(Validator.class);
PowerMockito.when(mockImpl, Validator.validateConnection() ).thenReturn(true);
PowerMock.replayAll(mockImpl);
Boolean resp = mockImpl.checkConnection();
PowerMock.verifyAll();
Validate.notNull(resp);
}
}
Use a Validator object instead of its static methods and inject the Validator into Class_Implementation (by constructor). This is called Dependency Injection. In your test you can inject a mock of the Validator.
public class Validator {
public boolean validateConnection() {
...
}
}
public class Class_Implementation {
private final Validator validator;
public Class_Implementation(Validator validator) {
this.validator = validator;
}
public boolean checkConnection() {
...
validator.validateConnection();
...
}
}
public public class Class_ImplementationTest {
#Test
public void validate_Connection() throws Exception {
Validator validator = Mockito.mock(Validator.class);
Mockito.when(validator.validateConnection()).thenReturn(true);
Class_Implementation impl = new Class_Implementation(validator);
boolean response = mockImpl.checkConnection();
Assert.assertTrue(response);
}
}
I made some additional changes to your code.
Don't return a Boolean object if there are only two states.
A Unit test tests on class and is named like the class with an additional Test prefix.
You don't need PowerMock if you have nice code.
Just mock your Class_Implementation (you should change the name and stick to Java naming standards by the way) and stub the validateConnection() method:
final Class_Implementation mock = mock(Class_Implementation.class);
when(mock.checkConnection()).thenReturn(true); // or false
But anyway, what you should mock is the interface to start with.