spock mocking stubbing
Zombando, stubbing e espionando com Spock:
Teste parametrizado no Spock Framework foi explicado em detalhes neste Série de tutoriais de treinamento em Spock .
Mocking e Stubbing são um dos blocos de construção mais essenciais de extensos testes de unidade. Suporte para mocking e legendagem é como a cereja do bolo para uma estrutura.
Para frameworks existentes como JUnit, JBehave, etc., o suporte para mocks e stubs não sai da caixa, portanto, requer que um desenvolvedor use bibliotecas de terceiros, como Mockito, PowerMock, EasyMock, etc., a fim de usá-los no testes de unidade.
como abrir arquivo dat em pdf
Para entender mocks e stubs e seus casos de uso, você pode dar uma olhada em nossa Série de Tutorial Mockito .
Neste tutorial, aprenderemos mais sobre os recursos embutidos de Mocking e Stubbing integrados à própria biblioteca Spock que, por sua vez, permitiria o uso da sintaxe Groovy mais fácil e, assim, reduz a necessidade de adicionar / incluir qualquer outro 3rdbibliotecas partidárias.
Você sempre pode incluir outras estruturas de simulação em seus testes, já que todo código Java válido também é código Groovy válido.
O que você aprenderá:
- Aplicação em Teste
- Zombando em Spock
- Stubbing em Spock
- Espionagem em Spock
- Conclusão
- Código-fonte do aplicativo
- Leitura recomendada
Aplicação em Teste
Vamos primeiro definir um exemplo de aplicativo Java, que iremos testar usando simulações e stubs no framework Spock.
Estaremos trabalhando em um aplicativo StudentGradeCalculator que obtém a pontuação total de um banco de dados abstraído para um determinado ID de aluno e tem uma lógica simples de atribuição de nota, dependendo do valor da pontuação total. Usaremos uma interface de banco de dados que possui poucos métodos para buscar e atualizar as pontuações e notas dos alunos.
O código do Aplicativo estará disponível na última seção deste tutorial.
Zombando em Spock
Vídeo tutorial
Nesta seção, veremos como instanciar e inicializar Mocks no framework Spock e como validar as interações no mock, ou seja, a validação das chamadas para os mocks aconteceu de acordo com as expectativas do método em teste.
Com Mocks, você não precisa fazer muitas configurações, mas pode validar as interações que aconteceram com os objetos mock fornecidos ao aplicativo em teste.
Com mocks, você pode fazer coisas como:
- Com quais argumentos os mocks foram chamados?
- Qual foi a contagem total de invocações etc?
- Verificar a ordem das simulações.
Vamos ver um exemplo simples de StudentGradeCalculator, onde fornecemos o objeto de implementação de banco de dados simulado e validamos as interações com o Mock. Tentaremos entender os recursos de simulação com exemplos simples.
Observe que todas as validações de interação devem acontecer no bloco “then” por convenção.
Abaixo está o código para o método em teste (que será chamado no “ quando: ' quadra)
public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; }
# 1) Validando as interações com argumentos exatos: vamos primeiro validar as interações com os argumentos exatamente esperados. Aqui, esperaremos que os métodos simulados sejam chamados com os argumentos exatos (de acordo com o fluxo de execução do método).
Aqui ' studentDatabase ”É o mock de uma interface de banco de dados para a qual estamos validando as interações.
def 'illustrate mocks for interaction verification with arguments'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade('123','C') 1*studentDatabase.getStudentGrade('123') }
Conforme mostrado acima, estamos validando com os argumentos exatos, de modo que a implementação simulada deve ter sido chamada com. Quaisquer alterações nesses argumentos farão com que o teste falhe e o log de erros mostre o motivo apropriado.
Vamos tentar mudar a nota em “ updateStudentGrade ”Para“ A ”em vez do realmente chamado“ C ”e veja qual erro obtemos quando o teste é executado.
Too few invocations for: 1*studentDatabase.updateStudentGrade('123','A') (0 invocations) Unmatched invocations (ordered by similarity): 1 * studentDatabase.updateStudentGrade('123', 'C') 1 * studentDatabase.getStudentScores('123')
Ele mostrará um erro como “Muito poucas invocações”, pois não pode encontrar a invocação Mock com os argumentos fornecidos.
como ver arquivos xml em word
#dois) Agora vamos ver como validar as interações Mock sem fornecer os valores reais do argumento, ou seja, o que estamos interessados é apenas em saber que o mock foi invocado no método, mas não com quais argumentos.
Esses tipos de requisitos são mais comuns ao escrever testes de unidade para o código de produção real, pois nem sempre é fácil identificar os argumentos reais que dependem essencialmente da lógica de negócios principal do aplicativo em teste.
A sintaxe é simples, você só precisa usar um sublinhado “_” para um argumento onde o valor real não é conhecido.
Por exemplo, para verificar qualquer valor de String, você pode apenas mencionar '_ Como corda ”No lugar de um argumento no teste e deve passar para qualquer valor de String (da mesma forma para outros tipos de dados primitivos e personalizados).
Vamos entender isso com um exemplo
def 'illustrate mocks for interaction verification with generic matchers'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) 1*studentDatabase.getStudentGrade('123') }
Um ponto importante a se notar aqui é que você sempre pode misturar e combinar quais argumentos são conhecidos e quais não são. Por exemplo, no exemplo abaixo, estamos validando a interação de um mock com os argumentos reais e o outro com os matchers soltos.
# 3) Por último, vejamos um cenário onde podemos verificar a ordem da invocação simulada, ou seja, a ordem em que as simulações foram chamadas quando o teste é executado.
Às vezes, é essencial validar o fluxo de eventos quando há vários colaboradores / simuladores envolvidos no aplicativo em teste e é útil entender e validar que os métodos foram chamados em uma sequência pré-determinada.
def 'illustrate mocks for validating order'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.getStudentGrade('123') then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) }
Isso pode ser alcançado simplesmente usando vários blocos “then:” na ordem das expectativas da sequência mock. Se a sequência mencionada não atender à ordem real de invocação, um erro detalhando “Ordem de invocação errada” será gerado.
Por exemplo, se eu mudar a ordem do acima então , a execução do teste gerará um erro conforme mostrado abaixo.
Wrong invocation order for: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) (1 invocation) Last invocation: studentDatabase.updateStudentGrade('123', 'C')
Stubbing em Spock
Vídeo tutorial
Exploramos tudo sobre simulação, agora vamos ver como definir Stubs nos objetos simulados. Stubbing nada mais é do que configurar respostas predefinidas ou prontas nas invocações Mock para testar os diferentes fluxos / cenários do aplicativo em teste.
Pense nisso como programar um mock para retornar um valor predefinido quando for chamado. Continuaremos com o mesmo aplicativo StudentGradeCalculator e criaremos stub nas chamadas da interface do banco de dados para testar diferentes cenários.
Um Stub é como um Mock que de certa forma emula o comportamento do objeto real. Você pode simplesmente chamá-lo como um Mock programado.
Sintaxe de stub
A sintaxe para o stub é 2 operadores de deslocamento para a direita - ou seja, “ >> '
Para definir um esboço em qualquer chamada, você pode defini-lo da seguinte maneira:
StubbedObject.StubbedMethod(//argumentList) >> “Stubbed Response”
Agora vamos entender os diferentes cenários de stub com exemplos.
# 1) Stubbing com parâmetros reais: Se os argumentos forem conhecidos com antecedência ou se você quiser definir stub apenas quando a chamada for com argumentos especificados, essa maneira de especificar stubs pode ser usada.
def 'illustrate stubs with exact matchers'() { given: studentDatabase.getStudentScores('123') >> (20F, 30F, 50F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' }
Aqui, você pode ver que o stub foi definido com um argumento exato, ou seja, StudentId, neste caso, como '123' (para qualquer outro valor, o stub não será chamado e haverá uma resposta padrão retornada).
# 2) Stubbing com matchers tolerantes: Se os argumentos não são conhecidos (ou não são importantes), então podemos mencioná-los vagamente como fizemos para os mocks e a sintaxe permanece a mesma, ou seja, o sublinhado “_”.
def 'illustrate stubs with loose matchers'() { given: studentDatabase.getStudentScores(_ as String) >> (20F, 30F, 10F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' }
# 3) Vejamos outro exemplo rápido em que configuramos o stub para lançar uma exceção.
Esses cenários são muito úteis para validar a lógica de tratamento de erros de um aplicativo em teste (como no mundo real, gerar todas as exceções na verdade não é possível, mas um esboço simples pode ser configurado para retornar qualquer exceção que quisermos e, em seguida, declarar em o bloco então).
qual é o melhor software de cópia de dvd
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) }
Espionagem em Spock
Os espiões são baseados em objetos reais ou seja, eles precisam da implementação da interface e não da interface abstrata em si. Spies são poderosos e podem permitir que você obtenha métodos reais chamados para o aplicativo em teste e verifique quais argumentos os métodos foram chamados.
Os espiões também permitem definir simulações parciais nas instâncias do objeto espiado. ou seja, suponha que você queira definir o comportamento de alguns métodos no objeto, então você pode e permite que o resto seja chamado como chamadas de método reais.
Normalmente, eles são úteis em uma situação em que pode haver alguns métodos de interface que não estão implementados e alguns outros que são totalmente funcionais. Portanto, você, como desenvolvedor, pode escolher fazer o stub dos métodos não implementados e chamar as implementações reais dos métodos funcionais.
Deve-se notar que, para objetos Espiados, a menos que stubs sejam definidos, o comportamento padrão será chamar a implementação real. Dito isso, espiões não devem ser chamados com frequência e toda a cobertura do cenário pode ser alcançada usando simulações e stubs e uma combinação deles.
Vamos ver alguns exemplos usando Spies na estrutura Spock usando o mesmo exemplo de StudentGradeCalculator (Nós criamos uma implementação real do StudentDatabase que é uma implementação na memória usando HashMap para ilustrar a chamada de métodos reais e o retorno de dados. O código estará disponível na última seção do tutorial):
# 1) Espiar usando uma combinação de chamadas de stub e métodos reais
def 'illustrate spies'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' 1*spiedStudentDatabase.getStudentGrade(_ as String) >> 'A' }
O exemplo acima ilustra a sintaxe para criar Spy usando a estrutura Spock. O stub é definido no próprio momento da declaração.
Além disso, as chamadas espiadas podem ser verificadas conforme ilustrado no bloco then (com correspondências de argumento soltas que podem ser definidas para qualquer argumento específico).
# 2) Espiar usando todas as chamadas de método reais
def 'illustrate spies with real method call'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' 1*spiedStudentDatabase.getStudentGrade('123') }
No exemplo acima, como não mencionamos nenhum comportamento em stub, todas as chamadas irão para a implementação real.
Conclusão
Neste tutorial, aprendemos tudo sobre as técnicas embutidas para Mock Stub e Spy usando a estrutura Spock. Spock torna isso mais fácil combinando esses recursos como parte da própria estrutura com uma sintaxe groovy mais legível junto com o código clichê menor.
Mocks, Stubs e Spies são usados extensivamente em testes de unidade para aumentar a cobertura e testar ou validar a lógica de negócios principal do aplicativo em teste.
Código-fonte do aplicativo
StudentReportGenerator.java - este é o método / aplicativo em teste
package app.studentScores; import java.util.List; public class StudentReportGenerator { public IStudentDatabase studentDatabase; public StudentReportGenerator(IStudentDatabase studentDatabase) { this.studentDatabase = studentDatabase; } public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; } }
IStudentDatabase.java - Interface de banco de dados
package app.studentScores; import java.util.List; public interface IStudentDatabase { List getStudentScores(String studentId); void updateStudentGrade(String studentId, String grade); String getStudentGrade(String studentId); }
StudentDatabase.java - implementação InMemory da interface IStudentDatabase.java
package app.studentScores; import java.util.*; public class StudentDatabase implements IStudentDatabase { private Map scoreMap; private Map gradeMap; public StudentDatabase() { this.scoreMap = new HashMap(); this.gradeMap = new HashMap(); scoreMap.put('123', Arrays.asList(40F, 30F, 30F)); scoreMap.put('456', Arrays.asList(10F, 10F, 30F)); gradeMap.put('123', 'C'); gradeMap.put('456', 'A'); } @Override public List getStudentScores(String studentId) { return scoreMap.get(studentId); } @Override public void updateStudentGrade(String studentId, String grade) { gradeMap.put(studentId,grade); } @Override public String getStudentGrade(String studentId) { return gradeMap.get(studentId); } }
Em nosso próximo tutorial, veremos como integrar a estrutura do Spock a outras estruturas e tecnologias de teste.
PREV Tutorial | PRÓXIMO Tutorial
Leitura recomendada
- Escrevendo testes de unidade com o Spock Framework
- Perguntas da entrevista de Spock com respostas (mais populares)
- Spock para integração e testes funcionais com selênio
- Teste baseado em dados ou parametrizado com Spock Framework
- Tutorial do Spock: Testando com Spock e Groovy
- Best FREE C # Tutorial Series: The Ultimate C # Guide for Beginners
- Teste de carga com tutoriais HP LoadRunner
- Funções de data e hora em C ++ com exemplos