How to test controller with springboot and sonarqube

1.5k Views Asked by At

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!

1

There are 1 best solutions below

0
On

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 and MockMvc and it's great integration with Spring Security to mock the authenticated user:

@Test
@WithMockUser("someUsername")
public void shouldAccessPartiesEndpoint() throws Exception {
  this.mockMvc
    .perform(delete("/parties"))
    .andExpect(status().isOk());
}

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 the TestRestTemplate or the WebTestClient and not MockMvc (uses a mocked servlet environment).