So I am working on my first Spring Boot project and I am stuck with my tests. I have looked a whole lot of examples up, but none of them seem to work.
This is a current test for my controller that I have:
@SpringBootTest
@AutoConfigureMockMvc
public class AddPartyTest {
@Autowired
private MockMvc mockMvc;
@Test
@DisplayName("Should Not give access to endpoint")
public void ShouldNotGiveAccess() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.get("/parties"))
.andExpect(MockMvcResultMatchers.status().is(401));
}
And it works, but on sonarqube I get that I have 0% code coverage and I can't seem to find a test that even gets this percentage above zero. Can anyone give me an example on how to write a good unit test for a controller and then I can figure it out myself based on your example.
This is my controller:
@RestController
@RequestMapping("/parties")
public class PartyController {
@Autowired
PartyService partyService;
@GetMapping("")
public List<Party> list(){
return partyService.listAllParties();
}
@GetMapping("/{id}")
public ResponseEntity<PartyDto> get(@PathVariable Integer id){
try{
PartyDto partyDto = partyService.getParty(id);
return new ResponseEntity<>(partyDto, HttpStatus.OK);
} catch (NoSuchElementException e){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@PostMapping("/")
public void add(@RequestBody PartyDto partyDto) throws ParseException {
partyService.saveParty(partyDto);
}
@PutMapping("/{id}")
public ResponseEntity<?> update(@RequestBody PartyDto partyDto, @PathVariable Integer id){
try{
PartyDto existParty = partyService.getParty(id);
partyDto.setId(id);
partyService.saveParty(partyDto);
return new ResponseEntity<>(HttpStatus.OK);
} catch (NoSuchElementException e){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Integer id){
partyService.deleteParty(id);
}
@DeleteMapping("/")
public ResponseEntity<HttpStatus> deleteAllParties() {
try {
partyService.deleteAll();
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
This is my Service (in case you need it):
@Service
@Transactional
public class PartyService {
@Autowired
private PartyRepository partyRepository;
@Autowired
private ModelMapper modelMapper;
public List<Party> listAllParties() {
return partyRepository.findAll();
}
public void saveParty(PartyDto partyDto){
Party party = convertToEntity(partyDto);
partyRepository.save(party);
}
public PartyDto getParty(Integer id) {
Party party = partyRepository.findById(id).orElse(null);
return convertToDto(party);
}
public void deleteParty(Integer id) {
partyRepository.deleteById(id);
}
public void deleteAll() {partyRepository.deleteAll();}
private PartyDto convertToDto(Party party) {
PartyDto partyDto = modelMapper.map(party, PartyDto.class);
return partyDto;
}
private Party convertToEntity(PartyDto partyDto) {
Party entity = new Party();
entity.setId(partyDto.getId());
entity.setName(partyDto.getName());
entity.setDescription(partyDto.getDescription());
entity.setIspartynational(partyDto.getIspartynational());
entity.setPartyLeader(partyDto.getPartyLeader());
return entity;
}
}
And my party DTO
@Getter
@Setter
public class PartyDto {
private Integer id;
private String name;
private String description;
private Boolean ispartynational;
private PartyLeader partyLeader;
}
My build.gradle
plugins {
id 'org.springframework.boot' version '2.4.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id "org.sonarqube" version "3.0"
id 'jacoco'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '15'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation('io.jsonwebtoken:jjwt:0.2')
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compile 'junit:junit:4.12'
implementation 'org.modelmapper:modelmapper:2.4.1'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
/**
* JUnit jupiter with mockito.
*/
testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '2.19.0'
/**
* Mockito for mocking.
*/
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0'
}
test {
useJUnitPlatform()
}
If you need my Party model or repository just comment it and I will add them! Thanks in advance!
The existing test verifies that an anonymous user gets an HTTP 401 response. The
MockMvc
request doesn't reach the controller as Spring Security blocks it before. Hence no code line is ever executed as part of your test and that's why you don't see any coverage.When it comes to testing your
@Controller
classes, take a look at@WebMvcTest
andMockMvc
and it's great integration with Spring Security to mock the authenticated user:For your
PartyService
a unit test with Mockito and JUnit should be enough.In case you want to write an integration test without any mocks, use
@SpringBootTest
and e.g. access your application with theTestRestTemplate
or theWebTestClient
and notMockMvc
(uses a mocked servlet environment).