如何通过服务测试获得超过 0% 的声纳覆盖率

问题描述

所以我是第一次使用 Sonarqube,我真的不知道如何为我的服务类编写一个好的单元测试,其中 sonarqube 的覆盖率不是 0%。

这是我要测试的我的服务类

import com.example.demo.DTO.PartyleaderDto;
import com.example.demo.Model.Partyleader;
import com.example.demo.Repository.PartyleaderRepository;
import org.modelmapper.modelmapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;

@Service
@Transactional
public class PartyleaderService {
    @Autowired
    private PartyleaderRepository partyleaderRepository;
    @Autowired
    modelmapper modelmapper;
    public List<Partyleader> listAllPartyleaders() {
        return partyleaderRepository.findAll();
    }

    public void savePartyleader(PartyleaderDto partyleaderDto) {
        Partyleader partyleader = convertToEntity(partyleaderDto);
        partyleaderRepository.save(partyleader);
    }

    public PartyleaderDto getPartyleader(Integer id) {
        Partyleader partyleader = partyleaderRepository.findById(id).orElse(null);
        return convertToDto(partyleader);
    }


    public void deletePartyleader(Integer id) {
        partyleaderRepository.deleteById(id);
    }
    public void deleteallleaders() {
        partyleaderRepository.deleteall();}

    private PartyleaderDto convertToDto(Partyleader partyleader) {
        PartyleaderDto partyleaderDto = modelmapper.map(partyleader,PartyleaderDto.class);
        return partyleaderDto;
    }
    private Partyleader convertToEntity(PartyleaderDto partyleaderDto) {
        Partyleader entity = new Partyleader();
        entity.setId(partyleaderDto.getId());
        entity.setName(partyleaderDto.getName());
        entity.setApperance(partyleaderDto.getApperance());

        return entity;
    }
}

这是我目前的测试课程:

@SpringBoottest
@AutoConfiguremockmvc
public class PartyleaderServiceTest {
    
    @Autowired
    private modelmapper modelmapper;

    @Test
    public void whenConvertPartyleaderEntityToPostDto_thenCorrect() {
        Partyleader partyleader = new Partyleader();
        partyleader.setId(1);
        partyleader.setName("Josse");
        partyleader.setApperance("Link of image");

        PartyleaderDto partyleaderDto = modelmapper.map(partyleader,PartyleaderDto.class);
        Assert.assertEquals(partyleader.getId(),partyleaderDto.getId());
        Assert.assertEquals(partyleader.getName(),partyleaderDto.getName());
        Assert.assertEquals(partyleader.getApperance(),partyleaderDto.getApperance());
    }

    @Test
    public void whenConvertPartyDtoToPartyEntity_thenCorrect() {
        PartyleaderDto partyleaderDto = new PartyleaderDto();
        partyleaderDto.setId(1);
        partyleaderDto.setName("Josse");
        partyleaderDto.setApperance("Link of image");

        Partyleader partyleader = modelmapper.map(partyleaderDto,Partyleader.class);
        Assert.assertEquals(partyleaderDto.getId(),partyleader.getId());
        Assert.assertEquals(partyleaderDto.getName(),partyleader.getName());
        Assert.assertEquals(partyleaderDto.getApperance(),partyleader.getApperance());
    }
}

这个测试提供了 0% 的覆盖率,我认为这是因为我没有使用服务 partyleaderService 类,但我似乎无法找到应该如何完成。有人可以帮我做 1 个测试,然后我可以自己弄清楚其余的。 提前致谢!

我的 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 {
    compile 'org.apache.httpcomponents:httpcore:4.4.1'
    compile 'org.apache.httpcomponents:httpclient:4.5'
    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'
    implementation 'org.eclipse.jgit:org.eclipse.jgit:5.4.2.201908231537-r'
    /**
     * JUnit jupiter with mockito.
     */
    testCompile group: 'org.mockito',name: 'mockito-junit-jupiter',version: '2.19.0'

    testCompile group: 'org.mockito',name: 'mockito-core',version: '2.19.0'
    testCompile group: 'org.springframework.security',name: 'spring-security-test',version: '5.1.6.RELEASE'
}

sonarqube{
    properties{
        property 'sonarjava.source','1.8'
        property 'sonar.java.coveragePlugin','jacoco'
        property 'sonar.jacoco.reportPaths','build/reports/jacoco/test/jacocoTestReport.xml'
    }
}
test {
    useJUnitPlatform()
}

解决方法

您编写了一个测试来验证 ModelMapper 是否正常工作。没有错,只是它是 ModelMapperTest 而不是 PartyLeaderServiceTest

要测试 PartyLeaderservice,您可以:

  • 编写单元测试,模拟所有其他依赖项

public class PartyLeaderServiceTest {

    @MockitoRule
    public var mockitoRule = new MockitoRule();

    @Mock
    private PartyLeaderRepository partyLeaderRepository;

    @Mock
    private ModelMapper modelMapper;

    @InjectMocks
    private PartyLeaderService partyLeaderService; // this is like calling new PartyLeaderService(partyLeaderRepository,modelMapper); 

    @Test
    void whenSavePartyLeader_thenCorrectPartyLeaderSaved() {
        // given
        var input = PartyLeaderDto.builder()
            .name("Josse")
            .appearance("Link of image")
            .build();

        // when
        partyLeaderService.savePartyLeader(input);


        // then
        verify(partyLeaderRepository).savePartyLeader(argThat(entity -> 
                entity.getName().equals("Josse")
                && entity.getAppearance().equals("Link of image"));
    }

    @Test
    void whenGetPartyLeader_ShouldReturnCorrectLeaderData() {
        // given
        def partyLeaderEntity = PartyLeader.builder()
            .name("Josse")
            .appearance("Link of image")
            .build();
        def partyLeaderDto = PartyLeaderDto.builder()
            .name("Josse")
            .appearance("Link of image")
            .build();
        when(partyLeaderRepository.findById(3L)).thenReturn(Optional.of(partyLeaderEntity));
        when(modelMapper.map(partyLeader,PartyLeaderDto.class)).thenReturn(partyLeaderDto);

        // when
        def result = partyLeaderService.getPartyLeader(3L);

        // then
        assertEqual(result,partyLeaderDto);
    }
...

}
  • 编写对 PartyLeaderService 的真实设置进行操作的集成测试:
public class PartyLeaderServiceTest {

    @Autowired
    private PartyLeaderService partyLeaderService;

    @Autowired
    private EntityManager em;

    @Test
    void whenSavePartyLeader_thenCorrectPartyLeaderSaved() {
        // given
        var input = PartyLeaderDto.builder()
            .name("Josse")
            .appearance("Link of image")
            .build();

        // when
        partyLeaderService.savePartyLeader(input);
        def createdPartyLeader = em.createQuery("SELECT l FROM PartyLeader l",PartyLeader.class).getSingleResult();

        // then
        assertEqual(createdPartyLeader.getName(),"Josse");
        assertEqual(createdPartyLeader.getAppearance(),"Link of image");
    }

    @Test
    void whenGetPartyLeader_ShouldReturnCorrectLeaderData() {
        // given
        def partyLeaderEntity = PartyLeader.builder()
            .name("Josse")
            .appearance("Link of image")
            .build();
        entityManager.persist(partyLeaderEntity); // or use @SqlScript to insert initial data into the db
        entityManager.flush();

        // when
        def result = partyLeaderService.getPartyLeader(partyLeaderEntity.getId());

        // then
        assertEqual(createdPartyLeader.getName(),"Link of image");
    }
...

}
  • 创建两种方法的混合,例如在单元测试中,您可以决定要模拟 partyLeaderRepository,但使用 ObjectMapper 的实际实现,因为模拟它太费力了。

选择哪个选项主要取决于几个因素:

  • 您正在测试的组件的逻辑是否非常复杂(如果是,请使用单元测试;另一方面,如果该组件仅调用其他组件——尤其是第三方组件,则集成测试可能更有价值)
  • ROTI(您在编写/维护测试方面付出了多少努力与它给您的信心水平,例如,通过集成测试,您可以测试事务边界是否正常工作;但是,您需要启动应用程序上下文,确保所有其他组件都处于正确状态,维护初始 DB 脚本等)
  • 所需的测试范围(单元测试仅测试一个组件,而所有其他依赖项都被模拟,以便它们的行为可预测;另一方面,如果 {{1 }} 或 ModelMapper 坏了)

一个常见的经验法则是为一个复杂逻辑组件编写多个单元测试,测试所有可能的路径,并进行一两个集成测试(例如一个愉快的路径和一个替代路径)以验证当您连接组件在一起。