Junit testing in a Spring / Maven project - java

I have a problem writing a Junit testcase for a Spring project. Its about the following method;
boolean doesUserIdExist(String userId){
if(userRepository.findOne(userId.toLowerCase()) != null) {
throw new userAlreadyExistsException("User with id: " + userId + " already exists")
return false;
}else{
return true;
}
}
Now in my jUnit I have something written like this..:
void compareDuplicateUserIdTest (){
UserService UserService = new UserService();
String lowercase = "test";
String uppercase = "Test";
boolean result = userService.doesUserIdExist(lowercase);
//Check the boolean result if its true
}
Since im using the findOne method it means that i'd have to check the String = "test" against the DB userId = "test". This is not the right way since it should work standalone without any records in the MongoDB database.
Now i've been reading about a framework like mockito to test this, but isn't this "too much" for such a simple method check? Can I remove the findOne part and just compare the strings?

You are facing a very common problem for unit testing where databases should not be involved (that would be integration testing), so... here is where Mockito is a great tool to use.
Using Mockito allows you to mock your database results and continue with the regular flow of your method, so you could do something like this:
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class UserService_doesUserIdExistTests
{
#Mock
private UserRepository userRepository;
#InjectMocks
private UserService userService;
#Test
void compareDuplicateUserIdTest() {
String lowercase = "test";
// Mocking the response for your method that uses external dependencies
when(userRepository.findOne(lowercase)).thenReturn(true); // You can mock the response you want using .thenReturn(...)
// Test your userService method (you can also debug it if needed)
boolean result = userService.doesUserIdExist(lowercase);
//Check the boolean result if its true
assertTrue(result);
}
}
I have not tested the code but shows the idea of testing userService.doesUserIdExist(...) method as a unit. Also this is quite helpful when you need to learn by debugging code.

No, it is not too much. The idea of Unit tests is to test method behaviour based on different inputs and insure it behaves as expected. It also documents your expectations and makes refactoring much less painful.
Imagine in the future somebody (may be even you) will decide to remove userRepository.findOne from doesUserExist method. Your unit test will fail and then the developer will have to figure out if the tests need to be changed due to rafactoring or the refactoring needs to be fixed.
I am not even sure what you'll be testing if you remove findOne method and how you are planning to do that.
Mockito and Spring make mocking really simple. All you need to do is define and initiate mock userRepository and then define the behaviour based on the input.

Related

How to test void using Unit Test?

I have the following method and I wrote a unit test in Java for this method. It is coveraged except from the if statement and I also need to test this part.
#InjectMocks
private ProductServiceImpl productService;
public void demoMethod(final List<UUID> productUuidList) {
if (productUuidList.isEmpty()) {
return;
}
final Map<ProductRequest, PriceOverride> requestMap = getPriceRequests(uuidList);
productService.updateByPriceList(priceRequestMap, companyUuid);
}
However, as the method execution is finalized and does not return anything when uuidList is empty, I cannot test this if block.
So:
How can I test this if block?
Should I create a new Unit Test method for testing this if block? Or should I add related assert lines to the current test method?
Update: Here is my test method:
#Test
public void testDemoMethod() {
final UUID uuid = UUID.randomUUID();
final List<Price> priceList = new ArrayList<>();
final Price price = new Price();
price.setUuid(uuid);
priceList.add(price);
productService.demoMethod(Collections.singletonList(uuid));
}
The general idea is that you don't want to test specific code, but you want to test some behaviour.
So in your case you want to verify that getPriceRequests and priceService.updateByPriceList are not called when passing in an empty List.
How exactly you do that depends on what tools you have available. The easiest way is if you already mock priceService: then just instruct your mocking liberary/framework to verify that updateByPriceList is never called.
The point of doing a return in your if condition is that the rest of the code is not executed. I.e., if this // code omitted for brevity was to be executed, the method would not fill it's purpose. Therefore, just make sure that whatever that code does, it was not done if your list is empty.
You have 3 choices:
Write a unit test with mocks. Mockito allows you to verify() whether some method was invoked.
Write a more high-level test with database. When testing Service Facade Layer this is usually a wiser choice. In this case you can obtain the resulting state of DB in your test to check whether it did what it had to.
Refactor your code to work differently
Check out Test Pyramid and How anemic architecture spoils your tests for more details.

Testing a service method by Unit Test? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have no test experience and try to test a method by a Unit Test. All the examples that I have a look at perform operations via uses mock values. I know, I will also use mock values with mockito that I use in my project. Here is my service method that I want to test:
ProductServiceImpl:
public List<ProductDTO> findAllByCategoryUuid(UUID categoryUuid) {
// code omitted
return result;
}
Here is my Unit Test class:
ProductServiceImplTest:
// ? #InjectMocks
#Autowired
ProductServiceImpl productService;
#Mock
ProductRepository productRepository;
#Test
public void testFindAllByCategoryUuid() {
UUID categoryUuid = UUID.randomUUID();
final List<Product> productList = new ArrayList<>();
for (int i = 0; i < size; i++) {
// create product by setting "categoryUuid" and add to productList
}
productRepository.saveAll(productList);
when(productService.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);
}
My questions:
1. Is the approach above is correct in order to test the service method? I think I should not deal with inside the service method and just pass categoryUuid and check the result of that method for testing? Is that true?
2. In test class, I used #Autowired to access service method, but I am not sure if I should #Mock. Is there any mistake?
Any help would be appreciated.
Update: I also create unit test using DiffBlue plugin and it generates a test method as shown below. But I think it seems to be as testing repository methods rather than the service method. Is not it?
#Test
public void testFindAllByCategoryUuid() {
when(this.productRepository.findAllByCategoryUuid((UUID) any()))
.thenReturn(new ArrayList<Product>());
assertTrue(this.productService.findAllByCategoryUuid(UUID.randomUUID())
.isEmpty());
verify(this.productRepository).findAllByCategoryUuid((UUID) any());
// ...
}
I am not an expert but I will try to answer you question
The general approach to unit test your method should be to test the output against all possible set of inputs.
in your specific case you can test
input: existing UUID output : NonNull List.
input: non existing UUID output : Empty List.
input: null : Empty List.
Now what you have done here is right you need to Autowire the class that you are writing test cases for and mock the dependencies in that class.
Only thing wrong is
when(productService.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);
should be
when(productRepository.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);
here you are mocking productRepository.findAllByCategoryUuid as your goal is to test the method in service class.
after this just add appropriate assert statements for all the conditions mentioned above.
Also I usually follow a rule whenever bug is logged against some code I try to cover that input and output case using assert in my Junit so that every-time I will test all the possible input and output scenarios.
The important things to remember while writing Junit Tests using Mockito
All class level #Runwith()
Test class should be with #InjectMocks
All tests should be annotated with #Test
Any external service should be Mocked with #Mock
Any calls going to DB or other services should be mocked and values should be returned accordingly.
You should have assertions to test your result.
I would write something like this :
#RunWith(MockitoJUnitRunner.class)
public class ProductServiceImplTest {
#InjectMocks
ProductServiceImpl productService;
#Mock
ProductRepository productRepository;
#Test
public void testFindAllByCategoryUuid() {
UUID categoryUuid = UUID.randomUUID();
final List<Product> productList = new ArrayList<>();
for (int i = 0; i < size; i++) {
// create product by setting "categoryUuid" and add to productList
}
when(productRepository.saveAll(ArgumentMatchers.any()).thenReturn(productList);
// Or below might work for newer version of test cases when we get Null Pointer Exp using older version of Junit test cases
//doReturn(productList).when(productRepository).saveAll(any(List.class));
List<ProductDTO> response = productService.findAllByCategoryUuid(categoryUuid);
Assert.assertNotNull(response);
Assert.assertEquals("Object Value", response.getXXX());
}
Writing unit tests against Service layer comes with drawbacks:
You violate encapsulation of the method-under-test. If it changes because you start to invoke different classes/methods - the test will break. Even though the method may be working correctly.
Because you intend to use mocking, partly your tests will be simply checking that your mocks are set up to pass the tests. So basically you'll be testing test logic.
It's usually more productive to move the logic down e.g. to the Model. Those classes could be then unit-tested without mocks. Then you could write a higher-level test (including DB) that checks that everything works together.
Reading:
Anemic architecture - enemy of testing
Building Test Pyramid to optimize automated testing

Spring Data JPA database changes after each method don't roll back in integration tests

I am using JUnit 5 with Spring boot 2.2.4 for integration testing my Spring Boot application code with MySQL 5.7 database but the changes that a previous test method makes to database aren't rolled back causing errors in the ones that follow.
I've tried answers from different stackoverflow posts such as adding #Transactional (from Spring), #Rollback (method as well as class level), #TestExecutionListeners(listeners = {TransactionalTestExecutionListener.class}). I've tested these options individually (as well as in combination) but adding them in the test code together.
This is my test code:
#DataJpaTest
#Transactional
#Rollback
#ExtendWith(SpringExtension.class)
#Import({UserUsecasesImpl.class})
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserUsecasesImplINTTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
#Spy
private UserRepository userRepository;
#Autowired
private UserUsecases userUsecases;
#Test
void injectedComponentsAreNotNull(){
assertNotNull(entityManager);
assertNotNull(userRepository);
assertNotNull(userUsecases);
}
#Test
#Rollback
void Create_EntityAlreadyExists_ReturnsException() {
// set up
User expected = UserFixture.user1.toBuilder().build();
// entityManager.persistAndFlush(expected);
userRepository.saveAndFlush(expected);
// execute
User actual = userUsecases.create(expected);
// verify
assertEquals(expected, actual);
verify(userRepository).findOne(any(Example.class));
verify(userRepository, never()).save(any(User.class));
verifyNoMoreInteractions();
}
#Test
void Find_WhenUserPresent_ReturnUser() {
// // set up
User expected = UserFixture.user1.toBuilder().build();
entityManager.persistAndFlush(expected);
// execute
User actual = userRepository.find(expected);
// verify
assertEquals(expected, actual);
}
#Test
// #Transactional
void Find_WhenUserNotPresent_ReturnNull() {
// // set up
User expected = UserFixture.user1.toBuilder().build();
// entityManager.persistAndFlush(UserFixture.user2.toBuilder().build());
// execute
User actual = userUsecases.find(expected);
// verify
assertNull(actual);
}
}
UserUsecasesImpl is the class that I am trying to test which contains usecases such as creating a user, finding a user, get all users, etc. There is no special configuration anywhere else in the code.
Please implement following fixes and let me know if problem still there:
Remove all #Transactional and #Rollback. Keep #DataJpaTest which by default makes all of your tests #Transactional and therefore by default will roll them all back.
don't use saveAndFlush. The flush part is flushing save operation into the database and most likely causes your issue. Use save instead. More here https://www.baeldung.com/spring-data-jpa-save-saveandflush
If you are expecting exception assert it with assertThrows.
In general try not to throw every single annotation you know into the mix at the same time. Add them one at a time as you need them, try to understand what they do.
What is UserUseCases and why does it do most of the logic I would expect the Repository class to do?

How to properly write JUnit tests?

I'm a bit confused on how to properly write unit tests for my application.
Actually i want to know if i have to rewrite an existed method and modify it for junit or just call my existed method and use assert.
So far, i use the second option, but i've came across with a problem.
For example, inside a Controller method i'm getting the currently logged in user and pass it to some services. If i call this method through JUnit it will show null exception because there is no logged in user.
So,
1) do i have to rewrite these kind of methods for testing purposes?
2) Is it proper to call existed methods and use assertion anyway?
Thanks
#RequestMapping(value="/like", method=RequestMethod.GET)
public String msgLike(#RequestParam("msgId") long messageId, #RequestParam("like") boolean like){
User user = new User();
user = this.userService.getUserByName(this.userService.getLoggedInUsername()); //NULL EXCEPTION HERE WHEN TESTING
if(!messageService.checkIfLiked(user, messageId)){
if(like){
messageService.insertLike(messageId);
messageService.insertMessageUserLike(user, messageId);
}
if(!like){
messageService.insertDislike(messageId);
messageService.insertMessageUserLike(user, messageId);
}
}
return "redirect:/home";
}
Actually i want to know if i have to rewrite an existed method and modify it for junit or just call my existed method and use assert.
Just call your exiting method, In fact junit is written before writing logic for calling method.
Example:
If you want to test int square(int num) method, which find square of given num,
So write Junits like this ,
#Test
squareTest() {
int square = objectName.square(3);
assertThat(square , is(equalTo(9)));
}
And when coding done like this,
int square(int a) {
result=a*a;
return result;
}
Run your Junit.
For your second question,
You will have to read Mocking.
In your case you are trying to test a rest controller which is slightly different when testing normal methods. For example the following method:
public MyClass{
public static int sum(int a, int b){
return a + b;
}
}
Could simply be tested by:
#Test
public void testSum(){
assertEquals(3, MyClass.sum(1, 2));
}
But in your case you require the controller api to be used properly. i.e. you require a logged in user. For this you would need to test your controllers by accessing them as users would. This provides some explanation on the process and this answer provides even more details.
Essentially what you are looking for is unit testing rest controllers.
What you need to do is in testing, supply a fake userService that will give the function a User with properties relevant for that test.
Before the test, you need to set this.userService to your FakeUserService.
It's hard to be more specific without seeing more of your code.
This is a good question because this is a common problem when learning to write unit tests. Solving this problem will help you write better code.

Should I repeat code in actual class in tests

I want to test that a specific method produces the expected result, but to do that I need to manipulate the input in the test as well.
class ToTest {
public String produceResponse(String input) {
// ....
encryptedIds = encryptIds(input)
output = doStuff(input, encryptedIds)
}
public encryptIds(input) {
....
}
}
In my test I need to check that produceResponse actually produces the expected response.
in order to do that I have to encrypt the ids in the input.
My question is: should I rewrite encryptIds in the test (so that I would have more controller on the result) or should I call encryptIds from the class itself.
Is there a better approach to solve this? I don't like that in my test I know what happens in the specific flow.
If I understand correctly, you would like to test produceResponse() with known encryptedIds as input.
You could do that without refactoring the code, but it would probably be a good idea to refactor it, so that's what I'm going to explain:
class ToTest {
private IdEncryptor encryptor;
public ToTest(IdEncryptor encryptor) {
this.encryptor = encryptor;
}
public String produceResponse(String input) {
String[] encryptedIds = encryptor.encryptIds(input);
return doStuff(input, encryptedIds);
}
}
Now you can unit-test IdEncryptor to test that it produces correct encrypted IDs based on a String input.
And to test the ToTest class, you can mock the IdEncryptor so that whatever the input it receives, it produces the encryptedIds you desire. For example with mockito:
IdEncryptor mockEncryptor = mock(IdEncryptor.class);
when(mockEncryptor.encryptIds(any(String.class)).thenReturn(new String[] {"a", "b"});
ToTest toTest = new ToTest(mockEncryptor);
String response = toTest.produceResponse("input");
// expect that the response is what you expect given "a", "b" as input of doStuff()
Never copy any production code into the unit test as it will get outdated at some point.
If both methods are public, they are part of the public API, so:
you should first unit test the correct behavior of the encryptIds(String) method
then unit test the produceResponse(String) method which will internally use the already tested encryptIds(String) method
If encryptIds(String) would not be part of the public API:
then it is internal implementation and helper method which is not unit testable
produceResponse(String) is then responsible for encryption as a side-effect:
you can still test it if you mark it package private (no modifier)
you can also change the implementation of the encryptIds(String) only for testing purposes
Is encrypting id's something that is integral to your system or not? As it stands this class takes some input and produces some output and as far as your test is concerned this is what's important, no more, no less.
What is the impact of not performing the encryption? If your doStuff method will just fail if it doesn't happen then it is an internal detail to your class-under-test and I wouldn't have the tests care about it at all. If it's a step that absolutely must be performed then I would refactor the code to verify that it absolutely has happened, maybe using a mock as #jb-nizet answered.
As for the general case of duplicating production code in tests, as #Crazyjavahacking stated you should not do this, but I have no issue with using production code from a test- maybe not at a unit level but definitely the higher up the system I go, e.g. when testing writing to a DB I will use the reading code to verify it's happened correctly, but will also have independent tests to verify the reading path as well

Categories

Resources