Test JPA repository for void functions and updates and deletes - java

I working on writing tests for a crud application. I need to test the service and repository for Delete and Update statements. How would I go about mocking the repository for delete and update since they won't be returning data?
For example:
#Override
public void makeUserActive(long userId) {
try {
Optional<UserEntity> userEntityList = usersJpaRepository.findById(userId);
UserEntity userEntity = userEntityList.get();
userEntity.setIsActive(1);
usersJpaRepository.save(userEntity);
} catch (Exception e) {
logger.error("Cant make active user", e);
}
}
How do i test the service that mocks this repository and also the repository itself since it wont be returning a value

The question is what is the thing you want to be tested?
If you would like to test your repository you can achieve this by using Springs #DataJpaTest. see Integration Testing With #DataJpaTest
If you would like to test the logic inside your makeUserActive-Method you must make sure to mock your repository.
Assuming the service which contains your makeUserActive-Method looks something like this:
public class UserService{
private final UsersJpaRepository usersJpaRepository;
public UserService(UsersJpaRepository usersJpaRepository) {
this.usersJpaRepository = usersJpaRepository;
}
public void makeUserActive(long userId){
// your code from your question
}
}
You could write your Unit Test like this:
#Test
void makeUserActiveTest(){
UsersJpaRepository repository = new InMemoryUsersJpaRepository();
UserEntity user = new UserEntity();
user = repository.save(user);
UserService service = new UserService(repository);
service.makeUserActive(user.getId());
Optional<UserEntity> activatedUser = repository.findById(user.getId());
assertTrue(activatedUser.isPresent());
assertEquals(1, activatedUser.get().isActive());
}
The InMemoryUsersJpaRepository is a self written Mock which will store all data in an internal Map. The code could look something like this:
public class InMemoryUsersJpaRepository extends UsersJpaRepository {
private Map<Long, UserEntity> users = new HashMap<>();
private Long idCounter = 1L;
#Override
public UserEntity save(UserEntity user) {
if(user.getId() == null){
user.setId(idCounter);
idCounter++;
}
users.put(user.getId(), user);
return user;
}
#Override
public Optional<UserEntity> findById(long userId) {
return Optional.of(users.get(userId));
}
}
This way you will test the logic of your makeUserActive-Method which is currently to simply set the isActivated Flag on you UserEntity.
Also I would like to warn you about the answer of Mensur Qulami.
The Code in his answer will lead to a passing test but I'am pretty sure it does not test the thing you want to be tested.
You should always test the expected and observable behaviour of your method.
In your case this would be the isActivated Flag that should be 1.
The fact that your makeUserActive-Method calls the findById and save Method of the UsersJpaRepository is a mere implementation detail and the testing of those generally lead to brittle tests.

For the methods returning void, you can simply verify that they have been called. Here's an example, that mocks both an object returning method and void returning method.
#ExtendWith(MockitoExtension.class)
class ServiceTest {
#Mock
private Repository repository;
#InjectMocks
private Service service; // assume that this is your class
#Test
void testMakeUserActive() {
// given:
final UserEntity userEntity = new UserEntity();
// mocks:
when(repository.findById(1)).thenReturn(Optional.of(userEntity));
// when:
service.makeUserActive(1);
// then:
verify(repository).findById(1);
verify(repository).save(userEntity);
}
}

Related

Injecting Mock of Repository into Service doesn't inject the proper mocked method (Spring, JUnit and Mockito)

tl;dr:
Seems like the Mock of the repository I created with custom behavior regarding the save method when injected loses the custom behavior.
Problem Description
I've been trying to test a Service in Spring. The method of interest in particular takes some parameters and creates a User that is saved into a UserRepository through the repository method save.
The test I am interest in making is comparing these parameters to the properties of the User passed to the save method of the repository and in this way check if it is properly adding a new user.
For that I decided to Mock the repository and save the param passed by the service method in question to the repository save method.
I based myself on this question to save the User.
private static User savedUser;
public UserRepository createMockRepo() {
UserRepository mockRepo = mock(UserRepository.class);
try {
doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
savedUser= (User) invocation.getArguments(0);
return null;
}
}).when(mockRepo).save(any(User.class));
} catch( Exception e) {}
return mockRepo;
}
private UserRepository repo = createMockRepo();
Two notes:
I gave the name repo in case the name had to match the one in the service.
There is no #Mock annotation since it starts failing the test, I presume that is because it will create a mock in the usual way (without the custom method I created earlier).
I then created a test function to check if it had the desired behavior and all was good.
#Test
void testRepo() {
User u = new User();
repo.save(u);
assertSame(u, savedUser);
}
Then I tried doing what I saw recommended across multiple questions, that is, to inject the mock into the service as explained here.
#InjectMocks
private UserService service = new UserService();
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
This is where the problems arise, the test I created for it throws a null exception when I try to access savedUser properties (here I simplified the users properties since that doesn't seem to be the cause).
#Test
void testUser() {
String name = "Steve";
String food = "Apple";
service.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
Upon debugging:
the service seems to have received the mock: debugged properties of the service
the savedUser is indeed null: debugged savedUser propert .
I decided to log the function with System.out.println for demonstrative purposes.
A print of my logging of the tests, demonstrating that the user test doesn't call the answer method
What am I doing wrong here?
Thank you for the help in advance, this is my first stack exchange question any tips for improvement are highly appreciated.
Instead of instanciating your service in the test class like you did, use #Autowired and make sure your UserRepository has #MockBean in the test class
#InjectMocks
#Autowired
private UserService service
#MockBean
private UserRepository mockUserRepo
With this, you can remove your setup method
But make sure your UserRepository is also autowired insider your Service
You should not need Spring to test of this. If you are following Spring best practicies when it comes to autowiring dependencies you should be able just create the objects yourself and pass the UserRepository to the UserService
Best practices being,
Constructor injection for required beans
Setter injection for optional beans
Field injection never unless you cannot inject to a constructor or setter, which is very very rare.
Note that InjectMocks is not a dependency injection framework and I discourage its use. You can see in the javadoc that it can get fairly complex when it comes to constructor vs. setter vs. field.
Note that working examples of the code here can be found in this GitHub repo.
A simple way to clean up your code and enable it to be more easily tested would be to correct the UserService to allow you to pass whatever implementation of a UserRepository you want, this also allows you to gaurentee immuability,
public class UserService {
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
public final UserRepository userRepository;
public User newUser(String name, String food) {
var user = new User();
user.setName(name);
user.setFood(food);
return userRepository.save(user);
}
}
and then your test would be made more simple,
class UserServiceTest {
private UserService userService;
private UserRepository userRepository;
private static User savedUser;
#BeforeEach
void setup() {
userRepository = createMockRepo();
userService = new UserService(userRepository);
}
#Test
void testSaveUser(){
String name = "Steve";
String food = "Apple";
userService.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
public UserRepository createMockRepo() {
UserRepository mockRepo = mock(UserRepository.class);
try {
doAnswer(
(Answer<Void>) invocation -> {
savedUser = (User) invocation.getArguments()[0];
return null;
})
.when(mockRepo)
.save(any(User.class));
} catch (Exception e) {
}
return mockRepo;
}
}
However, this doesn't add a lot of benefit in my opinion as you are interacting with the repository directly in the service unless you fully understand the complexity of a Spring Data Repository, you are after all also mocking networking I/O which is a dangerous thing to do
How do #Id annotations work?
What about Hibernate JPA interact with my Entitiy?
Do my column definitions on my Entitiy match what I would deploy against when
using something like Liquibase/Flyway to manage the database
migrations?
How do I test against any constraints the database might have?
How do I test custom transactional boundaries?
You're baking in a lot of assumptions, to that end you could use the #DataJpaTest documentation annotation that Spring Boot provides, or replicate the configuration. A this point I am assuming a Spring Boot application, but the same concept applies to Spring Framework applications you just need to setup the configurations etc. yourself.
#DataJpaTest
class BetterUserServiceTest {
private UserService userService;
#BeforeEach
void setup(#Autowired UserRepository userRepository) {
userService = new UserService(userRepository);
}
#Test
void saveUser() {
String name = "Steve";
String food = "Apple";
User savedUser = userService.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
}
In this example we've went a step further and removed any notion of mocking and are connecting to an in-memory database and verifying the user that is returned is not changed to what we saved.
Yet there are limitations with in-memory databases for testing, as we are normally deploying against something like MySQL, DB2, Postgres etc. where column definitions (for example) cannot accurately be recreated by an in-memory database for each "real" database.
We could take it a step further and use Testcontainers to spin up a docker image of a database that we would connecting to at runtime and connect to it within the test
#DataJpaTest
#Testcontainers(disabledWithoutDocker = true)
class BestUserServiceTest {
private UserService userService;
#BeforeEach
void setup(#Autowired UserRepository userRepository) {
userService = new UserService(userRepository);
}
#Container private static final MySQLContainer<?> MY_SQL_CONTAINER = new MySQLContainer<>();
#DynamicPropertySource
static void setMySqlProperties(DynamicPropertyRegistry properties) {
properties.add("spring.datasource.username", MY_SQL_CONTAINER::getUsername);
properties.add("spring.datasource.password", MY_SQL_CONTAINER::getPassword);
properties.add("spring.datasource.url", MY_SQL_CONTAINER::getJdbcUrl);
}
#Test
void saveUser() {
String name = "Steve";
String food = "Apple";
User savedUser = userService.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
}
Now we are accurately testing we can save, and get our user against a real MySQL database. If we took it a step further and introduced changelogs etc. those could also be captured in these tests.

AssertionError on comparing different types of an object when unit testing my list user by id service

I'm trying to test a service in my API that when given a certain id, it should return user that corresponds to that id. Pretty straightforward, but i'm getting the following error that should be simple to fix but have no idea how :
java.lang.AssertionError:
Expecting:
<User(id=89, name=null, email=null)>
and actual:
<Optional[User(id=89, name=null, email=null)]>
to refer to the same object
My test code is :
#RunWith(MockitoJUnitRunner.class)
public class ServiceDetailUserTest {
#Mock
private UserRepository userRepository;
#InjectMocks
private UserDetailService userDetailService;
#Test
public void when_given_id_it_should_return_user() {
User user = new User();
user.setId(89L);
when(userRepository.findById(user.getId())).thenReturn(Optional.of(user));
Optional<User> expected = userDetailService.listUser(user.getId());
assertThat(expected).isSameAs(user);
}
}
And my service code (not even sure if needed) is :
#Service
public class UserDetailService {
#Autowired
UserRepository repository;
public Optional<User> listUser(Long id) {
return repository.findById(id);
}
}
So, to compare my expected with my user, i need them to be the exact same type...but how?
I've seen variations of this test, with one using the expression assertThat(expected).isNotNull(); instead of assertThat(expected).isSameAs(user);, but i'm not sure if that's correct.
This should work:
#Test
public void when_given_id_it_should_return_user() {
User user = new User();
user.setId(89L);
Optional<User> userMock = Optional.of(user);
when(userRepository.findById(user.getId())).thenReturn(userMock);
Optional<User> expected = userDetailService.listUser(user.getId());
assertEquals(expected, userMock);
}
This compares two instances of different types, since expected is an Optional, and user is a User:
assertThat(expected).isSameAs(user);
Either unwrap expected.get(), or make user an Optional.

How can I properly mock a Principal object in Spring?

First of all, I have the following endpoint method present within a class called RecipeController:
#RequestMapping(value = {"/", "/recipes"})
public String listRecipes(Model model, Principal principal){
List<Recipe> recipes;
User user = (User)((UsernamePasswordAuthenticationToken)principal).getPrincipal();
User actualUser = userService.findByUsername(user.getUsername());
if(!model.containsAttribute("recipes")){
recipes = recipeService.findAll();
model.addAttribute("nullAndNonNullUserFavoriteRecipeList",
UtilityMethods.nullAndNonNullUserFavoriteRecipeList(recipes, actualUser.getFavoritedRecipes()));
model.addAttribute("recipes", recipes);
}
if(!model.containsAttribute("recipe")){
model.addAttribute("recipe", new Recipe());
}
model.addAttribute("categories", Category.values());
model.addAttribute("username", user.getUsername());
return "recipe/index";
}
As you can see above, the method takes as a second parameter a Principal object. When running the application, the parameter points to a non-null object as expected. It contains information about the user that is currently logged in within the application.
I have created a test class for the RecipeController called RecipeControllerTest. This class contains a single method called testListRecipes.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class RecipeControllerTest{
#Mock
private RecipeService recipeService;
#Mock
private IngredientService ingredientService;
#Mock
private StepService stepService;
#Mock
private UserService userService;
#Mock
private UsernamePasswordAuthenticationToken principal;
private RecipeController recipeController;
private MockMvc mockMvc;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
recipeController = new RecipeController(recipeService,
ingredientService, stepService, userService);
mockMvc = MockMvcBuilders.standaloneSetup(recipeController).build();
}
#Test
public void testListRecipes() throws Exception {
User user = new User();
List<Recipe> recipes = new ArrayList<>();
Recipe recipe = new Recipe();
recipes.add(recipe);
when(principal.getPrincipal()).thenReturn(user);
when(userService.findByUsername(anyString()))
.thenReturn(user);
when(recipeService.findAll()).thenReturn(recipes);
mockMvc.perform(get("/recipes"))
.andExpect(status().isOk())
.andExpect(view().name("recipe/index"))
.andExpect(model().attributeExists("recipes"))
.andExpect(model().attributeExists("recipe"))
.andExpect(model().attributeExists("categories"))
.andExpect(model().attributeExists("username"));
verify(userService, times(1)).findByUsername(anyString());
verify(recipeService, times(1)).findAll();
}
}
As you can see in this second snippet, I tried to mock the Principal object within the test class, using the UsernamePasswordAuthenticationToken implementation.
When I run the test, I get a NullPointerException, and the stacktrace points me to the following line from the first snippet of code:
User user = (User)((UsernamePasswordAuthenticationToken)principal).getPrincipal();
The principal object passed as a parameter to the listRecipes method from is still null, even though I tried to provide a mock object.
Any suggestions ?
Create a class that implements Principal:
class PrincipalImpl implements Principal {
#Override
public String getName() {
return "XXXXXXX";
}
}
Sample test:
#Test
public void login() throws Exception {
Principal principal = new PrincipalImpl();
mockMvc.perform(get("/login").principal(principal)).andExpect(.........;
}
Spring MVC is very flexible with controller arguments, which lets you put most of the responsibility of looking up information onto the framework and focus on writing the business code. In this particular case, while you can use Principal as a method parameter, it's usually much better to use your actual principal class:
public String listRecipes(Model model, #AuthenticationPrincipal User user)
To actually set the user for a test, you need to work with Spring Security, which means adding .apply(springSecurity()) to your setup. (Complications like this, by the way, are the main reason I dislike using standaloneSetup, as it requires you to remember to duplicate your exact production setup. I recommend writing actual unit tests and/or full-stack tests.) Then annotate your test with #WithUserDetails and specify the username of the test user.
Finally, as a side note this controller pattern can be simplified significantly with Querydsl, as Spring is able to inject a Predicate that combines all of the filter attributes you're looking up by hand, and then you can pass that predicate to a Spring Data repository.
Did you try using...?
#Test
#WithMockUser(username = "my_principal")
public void testListRecipes() {
...

how to use mocks properly

I have foolowing class which I would like to mock:
BusineesLayer:
/**
* Created by Alexandr on 14.05.2016.
*/
public class BusineesLayer {
public OrderModel orderModel;
public DbService dbService;
...
public BusineesLayer(OrderModel orderModel,DbService dbService) {
this.orderModel = orderModel;
dbService = dbService;
}
public BusineesLayer() {
}
public boolean checkItemsInDb(List<Items> items) throws HandleOrderExeption {
...
return result
}
public boolean handleOrder() throws HandleOrderExeption {
checkItemsInDb(orderModel.getItemsList());
boolean res =dbService.addOrder(orderModel.getItemsList(),
orderModel.getCustumerName(),
countTotalSum(orderModel.getItemsList())
);
return res;
}
}
I would like to test hanldeOrder() method and in order to make it less excess insted of checkItemsinDb() inside invoke "true";
So, my test looks like this:
#Test
public void handleorderTest() {
...
BusineesLayer layer = Mockito.mock(BusineesLayer.class);
layer.dbService = busineesLayer.dbService;
layer.orderModel = busineesLayer.orderModel;
Mockito.when(layer.checkItemsInDb()).thenReturn(true);
boolean res = layer.handleOrder();
assertThat(res, equalTo(true));
}
but it always return false and doesn't go through handlOrder() at all
Is any ways to solve it? Or how can I refactor my code to test it?
You do not test mocks, you use mocks to help you test.
I think you've just become confused at how you are using mocks. Mocks allow us to simulate and can responses to objects we are testing. If you're testing the handleOrder method, then you should mock anything that interacts with that method, in this case DbService and OrderModel.
#Test
public void handleorderTest() {
BusineesLayer layer = new BusineesLayer(); //we are testing this!
DbService dbService = Mockito.mock(DbService.class);
OrderModel orderModel = Mockito.mock(OrderModel.class);
layer.dbService = dbService;
layer.orderModel = orderModel;
Mockito.when(orderModel.getItemsList()).thenReturn(new ArrayList<Items>());
Mockito.when(dbService.foo()).thenReturn(true);
//mock up multiple calls so your service will provide true
boolean res = layer.handleOrder();
assertThat(res, equalTo(true));
//repeat for false, and so on
}
If, however, you are trying to test the dbService call, then you should create a test for it without the business layer at all. The business layer doesn't depend on anything except calls to other methods, so whether you use those real objects or mocked versions of those objects, the functionality should be the same. The business logic only appears to fail if DBService or OrderModel break, so you would test service and model separately (without involving the business layer) to test those.

JUnit test cases using mockito for Rest API

I am new to Mockito framework , I have a rest APi that connects my application to jasper server and do report related operations.I want to write junit test cases for rest API using mockito framework.
here i have class called Repositoryclient , Its constructor have instance of JasperServerInfo DAO class.
public class RepositoryClient {
public RepositoryClient(ServerInfo serverInfo) {
this.info = serverInfo;
try {
Session = Client.authenticate(info.username.trim(), info.password.trim());
}
catch (Exception e) {
}
}
public void Templates() { //this method brings list of present report in jasper server repository
try {
OperationResult<...> result = ....;
} catch (Exception e) {
INodeProcessor.logger.warn(e.toString());
throw Error.REPORT_TEMPLATE_LIST_ERROR.with();
}
}
So how to write JUnit test cases using mockito for this class please guide me through. Thank you in advance.
Well, the code could be improved to make it actually testable...
At the moment, there is no really good way to write unit tests for your code, since the constructor creates a JasperserverRestClient without any chance to change it. The least you can do is add another constructor (may be package access) to allow another JasperserverRestClient to be used. (Alternatively you could think about using a Factory pattern. but this might be to complicated.)
Then you could mock that...
JasperserverRestClient jasperServerClient = Mockito.mock( JasperserverRestClient.class );
RestClientSession session = Mockito.mock( RestClientSession.class );
Mockito.when( jasperServerClient.authenticate( "x", "y")).thenReturn( session );
RepositoryClient repositoryClient = new RepositoryClient(jasperServerClient);
This would at least allow you to test, that authenticate is called with the correct parameters via Mockito.verify.
Also it would allow you to test that the listTemplates method calls the session with the correct parameters (of course you'll need to so some more mocking there).
An additional constructor, assuming your tests are in the same package, would look like this:
RepositoryClient(JasperserverRestClient httpRestClient, JasperServerInfo serverInfo) {
this.info = serverInfo;
this.httpRestClient = httpRestClient;
try {
restClientSession = httpRestClient.authenticate(info.username.trim(), info.password.trim());
}
catch (Exception e) {
INodeProcessor.logger.warn(e.toString());
throw Error.REPOSITORY_CLIENT_ERROR.with();
}
}
This way you can inject a mocked instance of your JasperserverRestClient into your object.
A test of your listTemplates method would (addtionally) look like this...
X resourcesService = Mockito.mock( X.class ); // No clue what the resourcesService() method is supposed to return, fill that in here
Mockito.when ( restClientSession.resourcesService() ).thenReturn ( resourcesService );
...This will allow the part restClientSession.resourcesService() to work. Next...
Y resources = Mockito.mock( Y.class ); // Same thing here, don't know what resources() should return, insert that class here
Mockito.when( resourcesService.resources()).thenReturn ( resources );
This will allow the resources() call to work.
Next we do some trickery:
Mockito.when( resources.parameter( Mockito.anyString(), Mockito.anyString()).thenReturn(resources); // assuming that ResourceSearchParameter constant is a String
This will allow the parameter() calls to work by returning the same resources() object.
And so on... You will need to when(...).thenReturn(...) the search method to return a OperationResult<ClientResourceListWrapper>, etc. but that's the same stuff as above.
And in the end, we could verify that the methods were called with the right parameters...
Mockito.verify( resources, Mockito.times(1)).parameter(ResourceSearchParameter.FOLDER_URI, info.reportDirectory);
Mockito.verify( resources, Mockito.times(1)).parameter(ResourceSearchParameter.RECURSIVE, "false"
Mockito.verify( resources, Mockito.times(1)).search();
getting started example that i have :
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#Test(priority = 31, groups = "success")
public void mockExample() {
Category category1 = mock(Category.class);
when(category1.getName()).thenReturn("Yess!!!");
System.out.println(category1.getName());
}
will print:"Yess!!!"
you can read from here :
http://examples.javacodegeeks.com/core-java/mockito/mockito-hello-world-example/
#RunWith(SpringJUnit4ClassRunner.class)
public class UserControllerTest {
#InjectMocks
private UserController userController;
#Mock
private RequestAttributes attrubutes;
#Mock
private UserService userService;
private MockMvc mockMvc;
#Before
public void setup() {
RequestContextHolder.setRequestAttributes(attrubutes);
this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
#Test
public void getUserinfoDetails() {
String userId = "123";
String userName = "Test145";
List<UserDto> userDtoList = new ArrayList<>();
Mockito.when(userService.getAllUserInfo()).thenReturn(userDtoList);
Assert.assertNotNull(userController.getUserinfo());
Assert.assertNotNull(userDtoList);
Assert.assertNotNull(userId);
Assert.assertNotNull(userName);
}
#Test
public void getUserByIdDetails() {
String userId = "123";
UserDto userDto = new UserDto();
Mockito.when(userService.getUserByUserId(userId)).thenReturn(userDto);
Assert.assertNotNull(userController.getUserById(userId));
Assert.assertNotNull(userDto);
Assert.assertNotNull(userId);
}
}
===========================================================================
for reference use below link:(step by step explanation)
https://www.youtube.com/watch?v=yGLOexeJfKA&t=17s

Categories

Resources