Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Unit Test Coverage for Skills and endorsement API Methods #166

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.RDS.skilltree.unitTests;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import com.RDS.skilltree.dtos.RdsGetUserDetailsResDto;
import com.RDS.skilltree.models.Endorsement;
import com.RDS.skilltree.models.Skill;
import com.RDS.skilltree.repositories.EndorsementRepository;
import com.RDS.skilltree.services.EndorsementServiceImplementation;
import com.RDS.skilltree.services.external.RdsService;
import com.RDS.skilltree.viewmodels.EndorsementViewModel;
import com.RDS.skilltree.viewmodels.RdsUserViewModel;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class EndorsementsApiUnitTests {

@Mock private EndorsementRepository endorsementRepository;

@Mock private RdsService rdsService;

@InjectMocks private EndorsementServiceImplementation endorsementService;

@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this); // Initialize mocks
}

@Test
@DisplayName("To test getAllEndorsementsBySkillId() method")
public void testGetAllEndorsementsBySkillId() {
List<Endorsement> endorsements = new ArrayList<>();

Integer skillId = 1;

// initializing a skill
Skill skill1 = new Skill();
skill1.setId(skillId);
skill1.setName("python");

// initializing an endorsement
Endorsement endorsement1 = new Endorsement();
endorsement1.setId(1);
endorsement1.setSkill(skill1);
endorsement1.setEndorserId("456");
endorsement1.setEndorseId("123");
endorsement1.setMessage("Please approve my endorsement for this skill");

endorsements.add(endorsement1);

RdsGetUserDetailsResDto rdsGetUserDetailsResDto1 = new RdsGetUserDetailsResDto();
RdsUserViewModel rdsUserViewModel1 = new RdsUserViewModel();
rdsUserViewModel1.setId("123");
rdsGetUserDetailsResDto1.setUser(rdsUserViewModel1);

RdsGetUserDetailsResDto rdsGetUserDetailsResDto2 = new RdsGetUserDetailsResDto();
RdsUserViewModel rdsUserViewModel2 = new RdsUserViewModel();
rdsUserViewModel2.setId("456");
rdsGetUserDetailsResDto2.setUser(rdsUserViewModel2);

// Mock behaviour
when(endorsementRepository.findBySkillId(skillId)).thenReturn(endorsements);
when(rdsService.getUserDetails(endorsement1.getEndorseId()))
.thenReturn(rdsGetUserDetailsResDto1);
when(rdsService.getUserDetails(endorsement1.getEndorserId()))
.thenReturn(rdsGetUserDetailsResDto2);

Comment on lines +68 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we mocking the Db calls?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes Prakash, correct

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we mocking the db calls?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As repository is a dependency for that method and we wanted to test in isolated space for unit tests, I have mocked it and provided pre defined data

Copy link
Author

@raj1802 raj1802 Nov 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @prakashchoudhary07 could you please let me know, for the above explanation regarding the reporsitory mocking, if any chnages needed please let me know

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, if I don't have DB code, if queries are wrong also then tests will pass

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @prakashchoudhary07 so this part of db validation is being covered as part of integration testing, so should we include in unit tests as well. please let me know?

cc @yesyash

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I will still insist we do it.
Now I am not sure what the test is being written for here. Is it a unit or integration?
So if you were testing the class why are you invoicing the db?
And if some behavior changes, then also since things are mocked, will tests pass without any issues right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if you are only writing unit tests for a module, why involve different modules here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prakashchoudhary07

  1. We are writing unit tests for the different methods in this pull request.
  2. I have written unit tests for 4 methods in this pull request with different methods for each test.

And if you are only writing unit tests for a module, why involve different modules here

For the above question, for example, when writing unit tests for the skills API getAll() method, we are retrieving all the skills from the skill repository. So, the dependency for this method is the skillRepository class. To test the getAll method in isolation, I have mocked this class implementation.

Based on the above conversation, do you think? should we include DB code as well? if yes should we use an in-memory database, as if we include the real db, as the data will be dynamic we should think of any strategy for this? could you please advise.

cc @yesyash

// Act

List<EndorsementViewModel> endorsmentsBySkillId =
endorsementService.getAllEndorsementsBySkillId(skillId);

assert (endorsmentsBySkillId.size() == 1);
assertEquals("456", endorsmentsBySkillId.get(0).getEndorser().getId());
assertEquals("123", endorsmentsBySkillId.get(0).getEndorse().getId());
assertEquals("python", endorsmentsBySkillId.get(0).getSkill().getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
package com.RDS.skilltree.unitTests;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import com.RDS.skilltree.dtos.RdsGetUserDetailsResDto;
import com.RDS.skilltree.enums.SkillTypeEnum;
import com.RDS.skilltree.enums.UserRoleEnum;
import com.RDS.skilltree.enums.UserSkillStatusEnum;
import com.RDS.skilltree.exceptions.NoEntityException;
import com.RDS.skilltree.exceptions.SkillAlreadyExistsException;
import com.RDS.skilltree.models.JwtUser;
import com.RDS.skilltree.models.Skill;
import com.RDS.skilltree.models.UserSkills;
import com.RDS.skilltree.repositories.SkillRepository;
import com.RDS.skilltree.repositories.UserSkillRepository;
import com.RDS.skilltree.services.SkillServiceImplementation;
import com.RDS.skilltree.services.external.RdsService;
import com.RDS.skilltree.utils.GenericResponse;
import com.RDS.skilltree.viewmodels.CreateSkillViewModel;
import com.RDS.skilltree.viewmodels.RdsUserViewModel;
import com.RDS.skilltree.viewmodels.SkillRequestsWithUserDetailsViewModel;
import com.RDS.skilltree.viewmodels.SkillViewModel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

public class SkillsApiUnitTests {

@Mock private SkillRepository skillRepository;

@Mock private UserSkillRepository userSkillRepository;

@Mock private RdsService rdsService;

@Mock private Authentication authentication;

@Mock private SkillRequestsWithUserDetailsViewModel skillRequestsWithUserDetailsViewModel;

@InjectMocks
private SkillServiceImplementation skillService; // Class containing the getAll() method

@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this); // Initialize mocks

SecurityContext securityContext = mock(SecurityContext.class);
SecurityContextHolder.setContext(securityContext);
when(securityContext.getAuthentication()).thenReturn(authentication);
}

@Test
@DisplayName(
"Test the getAll() method returns all the created skills, where the skills return in a sorted order based on skill-Id.")
public void getAllSkillsHappyFlow() {

Skill skill1 = new Skill();
skill1.setId(1);
skill1.setName("Java");
skill1.setType(SkillTypeEnum.ATOMIC);

Skill skill2 = new Skill();
skill2.setId(2);
skill2.setName("Spring Boot");
skill2.setType(SkillTypeEnum.ATOMIC);

// Mock the repository's behavior
when(skillRepository.findAll()).thenReturn(Arrays.asList(skill1, skill2));

// Act: Call the method to be tested
List<SkillViewModel> result = skillService.getAll();

// Assert: Verify the result
assertEquals(2, result.size());
assertEquals("Java", result.get(0).getName());
assertEquals("Spring Boot", result.get(1).getName());

// Verify that the repository was called once
verify(skillRepository, times(1)).findAll();
}

@Test
@DisplayName("Test the create() skill method, which returns the created skill data")
public void testCreateSkill_Success() {
// Arrange: Mock request and setup mocks
CreateSkillViewModel createSkill = new CreateSkillViewModel();
createSkill.setName("python");
createSkill.setType(SkillTypeEnum.ATOMIC);

// Mock JwtUser with rdsUserId and role
JwtUser jwtUser = new JwtUser("123", UserRoleEnum.USER);

RdsUserViewModel viewModel = new RdsUserViewModel();
viewModel.setId("123");

RdsGetUserDetailsResDto userDetails = new RdsGetUserDetailsResDto();
userDetails.setUser(viewModel);

Skill skillEntity = new Skill();
skillEntity.setId(5);
skillEntity.setName("python");
skillEntity.setType(SkillTypeEnum.ATOMIC);
skillEntity.setCreatedBy(userDetails.getUser().getId());

SkillViewModel expectedViewModel = SkillViewModel.toViewModel(skillEntity);

// Mock behavior

when(authentication.getPrincipal()).thenReturn(jwtUser); // Return mock JwtUser
when(rdsService.getUserDetails(jwtUser.getRdsUserId()))
.thenReturn(userDetails); // Use rdsUserId
when(skillRepository.saveAndFlush(any(Skill.class))).thenReturn(skillEntity);

when(skillRepository.existsByName("python")).thenReturn(false);

// Act: Call the method
SkillViewModel actualViewModel = skillService.create(createSkill);

// Assert: Verify the result
assertNotNull(actualViewModel);
assertEquals(expectedViewModel.getId(), actualViewModel.getId());
assertEquals(expectedViewModel.getName(), actualViewModel.getName());
assertEquals(expectedViewModel.getType(), actualViewModel.getType());

// Verify that the repository and service were called correctly
verify(skillRepository, times(1)).existsByName("python");
verify(skillRepository, times(1)).saveAndFlush(any(Skill.class));
}

@Test
@DisplayName("Test the create() skill method, when an already present skill is being added")
public void testCreateSkill_AlreadyExists() {
// Arrange
CreateSkillViewModel createSkill = new CreateSkillViewModel();
createSkill.setName("python");
createSkill.setType(SkillTypeEnum.ATOMIC);

// Mock behavior
when(skillRepository.existsByName("python")).thenReturn(true);

// Act & Assert: Verify that exception is thrown
SkillAlreadyExistsException exception =
assertThrows(
SkillAlreadyExistsException.class,
() -> skillService.create(createSkill),
"Skill with name " + createSkill.getName() + "already exists");

assertEquals("Skill with name python already exists", exception.getMessage());

// Verify repository was called once, but saveAndFlush wasn't called
verify(skillRepository, times(1)).existsByName("python");
verify(skillRepository, never()).saveAndFlush(any(Skill.class));
}

// To be modified

@Test
@DisplayName("To Test the approveRejectSkillRequest() method, when action is approved")
public void testApproveRejectSkillRequest() {

// Arrange
Integer skillId = 1;
String endorseId = "123";
UserSkillStatusEnum action = UserSkillStatusEnum.APPROVED;

Skill skill1 = new Skill();
skill1.setId(1);

List<UserSkills> existingSkillRequest = new ArrayList<>();
UserSkills userSkill1 = new UserSkills();
userSkill1.setId("1");
userSkill1.setUserId("123");
userSkill1.setStatus(UserSkillStatusEnum.PENDING);
userSkill1.setSkill(skill1);
existingSkillRequest.add(userSkill1);

when(userSkillRepository.findByUserIdAndSkillId(userSkill1.getUserId(), skill1.getId()))
.thenReturn(existingSkillRequest);
when(userSkillRepository.save(userSkill1)).thenReturn(userSkill1);

// Act: Call the method
GenericResponse<String> response =
skillService.approveRejectSkillRequest(skillId, endorseId, action);

// Assert
assertNotNull(response);
assertEquals("approved", response.getMessage()); // Depending on the action
assertEquals(UserSkillStatusEnum.APPROVED, userSkill1.getStatus());
}

@Test
@DisplayName("To Test the approveRejectSkillRequest() method, when action is rejected")
public void testApproveRejectSkillRequest_Reject() {
// Arrange
Integer skillId = 2;
String endorseId = "456";
UserSkillStatusEnum action = UserSkillStatusEnum.REJECTED;

Skill skill1 = new Skill();
skill1.setId(2);

UserSkills mockSkillRequest = new UserSkills();
mockSkillRequest.setSkill(skill1);
mockSkillRequest.setUserId(endorseId);
mockSkillRequest.setStatus(UserSkillStatusEnum.PENDING);

List<UserSkills> existingSkillRequest = new ArrayList<>();
existingSkillRequest.add(mockSkillRequest);

// Mock repository behavior
when(userSkillRepository.findByUserIdAndSkillId(endorseId, skillId))
.thenReturn(existingSkillRequest);
when(userSkillRepository.save(mockSkillRequest)).thenReturn(mockSkillRequest);

// Act
GenericResponse<String> response =
skillService.approveRejectSkillRequest(skillId, endorseId, action);

// Assert
assertNotNull(response);
assertEquals("rejected", response.getMessage()); // Depending on the action
assertEquals(UserSkillStatusEnum.REJECTED, mockSkillRequest.getStatus());

// Verify that the save method was called
verify(userSkillRepository, times(1)).save(mockSkillRequest);
}

@Test
@DisplayName("To Test the approveRejectSkillRequest() method, when no skill requests are present")
public void testApproveRejectSkillRequest_NoEntityException() {
// Arrange
Integer skillId = 3;
String endorseId = "789";
UserSkillStatusEnum action = UserSkillStatusEnum.APPROVED;

// Mock repository behavior for no entity found
when(userSkillRepository.findByUserIdAndSkillId(endorseId, skillId))
.thenReturn(new ArrayList<>());

// Act & Assert
assertThrows(
NoEntityException.class,
() -> {
skillService.approveRejectSkillRequest(skillId, endorseId, action);
});

// Verify that the save method was never called
verify(userSkillRepository, never()).save(any(UserSkills.class));
}
}
Loading