Este é o suficiente para começar com um tutorial detalhado sobre testes simulados

1. O que é um teste simulado

1.png

O teste simulado é usar um objeto virtual (objeto simulado) para criar um método de teste para testar alguns dados/cenários mais complexos que não são fáceis de construir ou obter na atividade de teste .

2. Por que testar simulado

Mock é resolver o problema que é difícil de desenvolver e testar devido ao acoplamento entre diferentes unidades. Portanto, o Mock pode aparecer não apenas em testes de unidade , mas também em testes de integração e testes de sistema .

A maior função do Mock é te ajudar a decompor o acoplamento dos testes de unidade.Se seu código possui dependências de outra classe ou interface, ele pode te ajudar a simular essas dependências e te ajudar a verificar o comportamento das dependências chamadas.

3. Cenários de aplicativos simulados

1. É necessário separar a unidade atual em teste de seus módulos dependentes, construir um ambiente de teste independente e não dar atenção aos objetos dependentes da unidade em teste, mas apenas focar na lógica funcional da unidade em teste.

2. O módulo do qual a unidade em teste depende não foi desenvolvido A, e a unidade em teste precisa contar com o valor de retorno do módulo para processamento subsequente.

3. Nos projetos front-end e back-end, antes da conclusão do desenvolvimento da interface back-end, a depuração conjunta da interface

4. A interface do projeto upstream dependente ainda não foi desenvolvida e o teste de depuração conjunta da interface é necessário

5. Os objetos dos quais a unidade em teste depende são difíceis de simular ou possuem estruturas complexas

Por exemplo: Existem muitas condições anormais no negócio de pagamentos, mas simular tais condições anormais é muito complicado ou impossível de simular.

4. Exemplo de código

Novo projeto de teste

package com.echo.mockito;
 
public class demo {
 
    //新建一个测试方法
    public int add(int a,  int b){
        return a + b;
    }
}

Crie um método de teste simulado

Selecione a classe de teste, clique com o botão direito e selecione gerar

2.png

teste de clique

3.png

Após clicar em ok, o método de teste correspondente será gerado no diretório de teste, correspondente ao diretório real

4.png

5. Descrição do método de parâmetro

@BeforeEach

É usado para preparar antes do teste. Muitas configurações de ambiente ou configurações básicas serão construídas antes do teste, que podem ser definidas aqui.

@Após cada

Para configuração pós-teste.

@Zombar

As anotações podem ser entendidas como um substituto para o método simulado, ao invés de seguir o método real e simular o comportamento do método real. Ao usar essa anotação, use o método MockitoAnnotations.openMocks(this) para fazer a anotação entrar em vigor.

@Espião

1. O objeto espião seguirá o método real, mas o objeto fictício não.

2. O parâmetro do método spy é a instância do objeto, e o parâmetro do mock é a classe.

@InjectMocks

Usado para injetar variáveis ​​simuladas marcadas com @Mock em classes de teste.

MockitoAnnotations.openMocks(this)

Ligue o mock e teste-o com as duas anotações acima. Geralmente é colocado em @BeforeEach e ativado antes do teste, para que não precise ser ativado em todos os métodos.

Mockito.when(demo.add(1,2)).thenReturn(3): empilhamento

O núcleo simulado pode definir o resultado do método a ser testado, de modo que o resultado da execução do método real seja ignorado e os testes subsequentes sejam executados com base no resultado do empilhamento.

Mockito.when(demo.add(1,2)).thenThrow(new RuntimeException());

Usado para simular exceções.

Assertions.assertEquals(3,demo.add(1,2)): Asserções

O principal meio de teste, no qual os resultados são julgados. (valor esperado, valor real).

6. Teste simples

O método de configuração do teste de empilhamento retorna 4, mas a execução real é 3 e o teste falha.

5.png

Nenhum teste de empilhamento Por ser um método de espionagem, o método real será usado e o teste será aprovado.

6.png

Se for um método simulado, se não houver empilhamento, haverá um valor padrão e o teste falhará. Você pode tentar.

7.png

7. Descrição do método de teste

Veja o código para chamadas detalhadas, geralmente da seguinte forma:

  • Teste de empilhamento

  • teste de exceção

  • chamada de método real

package com.echo.mockito;
 
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
 
 
class demoTest {
 
     @Mock
     demo demo;
     //测试前开启mock
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
 
    @Test
    void add() {
        //mock 打桩,就是不管真实的方法如何执行,我们可以自行假设该方法执行的结果
        //后续的测试都是基于打桩结果来走
       // Mockito.when(demo.add(1,2)).thenReturn(4);
       // Assertions.assertEquals(3,demo.add(1,2));
        //当测试方法出现异常,测试方法  如果有try{}catch{} 则可以测试异常是否正常
        //Mockito.when(demo.add(1,1)).thenThrow(new RuntimeException());
        //调用真实的方法
        Mockito.when(demo.add(1,1)).thenCallRealMethod();
        Assertions.assertEquals(2,demo.add(1,1));
    }
 
    @AfterEach
    void after(){
        System.out.println("测试结束");
    }
}

8. Método estático simulado

Na versão anterior, não era permitido simular e testar métodos estáticos.Se você precisar testar métodos estáticos, precisará substituir as novas dependências fictícias e comentar as dependências atuais, pois haverá conflitos.

O teste de método estático deve usar a classe MockedStatic do mock para construir o método de teste.

  <!--   <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.6.1</version>
        </dependency>
-->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>4.3.1</version>
            <scope>test</scope>
        </dependency>
package com.echo.mockito.Util;
 
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
 
import java.util.Arrays;
 
import static org.junit.jupiter.api.Assertions.*;
 
class StaticUtilsTest {
 
    @BeforeEach
    void setUp() {
    }
 
    // 有参静态方法构建
    @Test
    void range() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));
        Assertions.assertTrue(StaticUtils.range(2,6).contains(11));
    }
    // 无参静态方法构建
    @Test
    void name() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(StaticUtils::name).thenReturn("dhmw");
        Assertions.assertEquals("dhmw",StaticUtils.name());
 
    }
}

Problema: Não há problema na execução de um único método, mas ao executarmos todos os métodos da classe, verificamos que é reportado um erro.

A simulação estática já está registrada no thread atual Para criar uma nova simulação, o registro da simulação estática existente deve ser cancelado

Isso significa que cada método precisa ter seu próprio objeto fictício estático, que não pode ser compartilhado. Quando executados juntos, o primeiro método ocupa o objeto e o segundo método não tem como ocupá-lo.

8.png

Solução: Feche o objeto simulado demo.close() logo após a execução de cada método. Equivalente a um singleton. Solte-o após o uso e o próximo método poderá ser usado.

  @Test
    void range() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));
        Assertions.assertTrue(StaticUtils.range(2,6).contains(11));
       //关闭
        demo.close();
    }
    // 无参静态方法构建
    @Test
    void name() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(StaticUtils::name).thenReturn("dhmw");
        Assertions.assertEquals("dhmw",StaticUtils.name());
        //关闭
        demo.close();
    }

9. Melhore a cobertura do teste

Caso: No sistema de estatísticas de dados, o pusher local insere o nome do cliente e o número do celular e, finalmente, constrói um objeto de usuário e o armazena na tabela de dados.

O código do negócio é o seguinte:

package com.echo.mockito.service.impl;
 
import com.echo.mockito.dao.UserDao;
import com.echo.mockito.service.RegistrationService;
import com.echo.mockito.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
 
import javax.xml.bind.ValidationException;
import java.sql.SQLException;
 
public class RegistrationServiceImpl implements RegistrationService {
     @Autowired
     UserDao userDao;
    @Override
    public User register(String name, String phone) throws Exception {
        if (name == null || name.length() == 0){
            throw new ValidationException("name 不能为空");
        }
        if (phone == null || phone.length() ==0 ){
            throw new ValidationException("phone 不能为空");
        }
        User user;
        try {
              user = userDao.save(name,phone);
        }catch (Exception e){
            throw  new Exception("SqlException thrown" + e.getMessage());
        }
        return user;
    }
}
package com.echo.mockito.dao;
 
import com.echo.mockito.vo.User;
 
public class UserDao {
 
    public User save(String name,String phnoe){
        User user = new User();
        user.setName(name);
        return user;
    }
}

Para gerar o código de teste correspondente, há várias questões a serem consideradas neste momento.

1. A classe a ser testada é RegistrationServiceImpl, mas como o userDao nela é injetado na classe de teste?

2. Devido à necessidade de cobertura de teste, é necessário considerar que existem várias situações na classe de teste que precisam ser testadas.

(1): Dois ifs lançam duas exceções, e há um total de 2 situações a serem testadas.

(2): Salvar o banco de dados é dividido em salvamento normal e salvamento anormal, um total de 2 casos de teste.

Em resumo, o teste para esses quatro casos deve ser feito para cobrir todo o código.

Da mesma forma, geramos casos de teste.Existem instruções detalhadas no código e existem casos de teste para cada caso.

package com.echo.mockito.service.impl;
 
import com.echo.mockito.dao.UserDao;
import com.echo.mockito.vo.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
 
import javax.xml.bind.ValidationException;
 
import java.sql.SQLException;
 
import static org.junit.jupiter.api.Assertions.*;
 
class RegistrationServiceImplTest {
 
  @InjectMocks     //RegistrationServiceImpl 实例中注入@Mock标记的类,此处是注入userDao
    @Spy
    private RegistrationServiceImpl registrationService;
    @Mock
    private  UserDao userDao;
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
 
    @Test
    void register() throws Exception {
        // ------------------  第一种 name  异常情况   测试   start ------------------------
        String name = null;
        String phone = "1234";
        try {
          registrationService.register(name,phone);
        }catch (Exception e){
        Assertions.assertTrue(e instanceof ValidationException);
        }
 
        // ------------------  name  异常情况   测试   end  ------------------------
 
        // ------------------  第二种 phone  异常情况   测试   start  ------------------------
        name = "111";
        phone = null;
      try {
        registrationService.register(name,phone);
      }catch (Exception e){
        Assertions.assertTrue(e instanceof ValidationException);
      }
        // ------------------  phone  异常情况   测试   start  ------------------------
 
 
        // ------------------  第三种 userDao.save   正常情况   测试   start  ------------------------
      name = "111";
      phone = "111";
        //正常保存测试  打桩  走真实的方法
        Mockito.when(userDao.save(name,phone)).thenCallRealMethod();
        User user =  registrationService.register(name,phone);
        Assertions.assertEquals("111",user.getName());
        // ------------------  userDao.save   正常情况   测试   end  ------------------------
 
        // ------------------   第四种 userDao.save   异常情况   测试   start  ------------------------
      //异常保存测试  打桩   通过thenThrow 抛出异常  测试异常是否被捕获
      Mockito.when(userDao.save(name,phone)).thenThrow(new RuntimeException());
      try {
         registrationService.register(name,phone);
      }catch (Exception e){
        Assertions.assertTrue(e instanceof Exception);
      }
// ------------------  userDao.save   异常情况   测试   end  ------------------------
    }
}

Conforme mostrado acima: A primeira questão mencionada acima, como injetar variáveis ​​membro na classe de teste, pode ser feita através da anotação @InjectMocks.

O método de cobertura de teste é o seguinte:

9.png

Os resultados do teste são os seguintes:

1. A parte direita exibirá a cobertura do teste.

2. A cor verde do código real representa o teste coberto, e o vermelho representa o teste não coberto.

11.png

Se todos os casos de teste forem 100% cobertos, o resultado será o seguinte:

12.png

Em resumo, o método de teste de cobertura é resumido da seguinte forma:

1. De acordo com o código empresarial, analise todas as situações que precisam ser testadas.

2. Escreva códigos de teste específicos de acordo com diferentes situações de teste.

3. Para cada situação, você pode escrever códigos de teste específicos e, em seguida, esgotar todas as situações de inventário empilhando, afirmando, etc.

pergunta:

1. Se o nível do método de código real lançar uma exceção, da mesma forma, o método de teste também deve lançar exceções no nível do método, caso contrário, o teste relatará um erro.

    @Test
    void register() throws Exception {

 2. O banco de dados salvo é dividido em normal e anormal. Em seguida, teste primeiro a ramificação normal e, em seguida, teste a ramificação anormal. Se a ordem for invertida, o teste lançará uma exceção primeiro e a ramificação normal não será executada, o que levará a uma cobertura de teste incompleta.


A seguir estão as informações de suporte. Para amigos que fazem [teste de software], deve ser o warehouse de preparação mais abrangente e completo. Este warehouse também me acompanhou na jornada mais difícil. Espero que possa ajudar você também!

Applet de entrevista de teste de software

O banco de questões de teste de software está esgotado por milhões de pessoas! ! ! Quem é quem sabe! ! ! O miniprograma de quiz mais completo de toda a rede, você pode usar seu celular para fazer os quizzes, no metrô ou no ônibus, enrola!

As seguintes seções de perguntas da entrevista são abordadas:

1. Teoria básica de teste de software, 2. web, app, teste de função de interface, 3. rede, 4. banco de dados, 5. linux

6. web, aplicativo, automação de interface, 7. teste de desempenho, 8. noções básicas de programação, 9. perguntas de entrevista de hr, 10. perguntas de teste aberto, 11. teste de segurança, 12. noções básicas de computador

Método de aquisição de informações:

Acho que você gosta

Origin blog.csdn.net/myh919/article/details/131831333
Recomendado
Clasificación