Artigo original: An Introduction to Jasmine Unit Testing
Escrito por: Ahmed Bouchefra
O Jasmine é a biblioteca do JS mais popular para testes unitários de aplicações para a web. Neste tutorial, projetado para iniciantes, apresentaremos um guia rápido e completo para testes com o Jasmine.
Você será apresentado ao Jasmine, um framework de testes popular orientado a comportamentos para o JavaScript. Também veremos um exemplo prático simples de como escrever testes unitários com o Jasmine, que pode ajudá-lo a verificar facilmente se há bugs em seu código.
Em resumo, veremos como escrever suítes de teste, especificações e expectativas, além de como aplicar matchers (comparadores) integrados do Jasmine ou criar seus próprios matchers personalizados.
Também veremos como você pode agrupar suítes para organizar seus testes para bases de código mais complexas.
Apresentando o Jasmine
O Jasmine é um framework de desenvolvimento orientado a comportamentos (em inglês, BDD ou behavior-driven development). Com ele, você escreve testes antes de escrever o código real. Ele é muito popular para testes unitários de aplicações em JavaScript. Ele fornece utilitários que podem ser usados para executar testes automatizados para código síncrono e assíncrono.
O Jasmine tem muitos recursos:
- Ele é rápido, tem baixa sobrecarga e nenhuma dependência externa.
- É uma biblioteca completa e oferece tudo o que você precisa para testar seu código.
- Está disponível para Node e para o navegador.
- Pode ser usado com outras linguagens, como Python e Ruby.
- Não requer o DOM.
- Fornece uma sintaxe limpa e fácil de entender e também uma API rica e direta.
- Podemos usar linguagem natural para descrever os testes e os resultados esperados.
O Jasmine é uma ferramenta de código aberto disponível sob a licença permissiva do MIT. No momento da redação deste artigo, a versão principal mais recente é a Jasmine 3.0, que fornece novos recursos e algumas mudanças significativas. A versão 2.99 do Jasmine fornecerá diferentes avisos de descontinuação para suítes que têm comportamento diferente na versão 3.0, o que facilitará a migração dos desenvolvedores para a nova versão.
Você pode ler sobre os novos recursos e mudanças significativas neste documento.
Usando o Jasmine
Você pode usar o Jasmine de várias maneiras diferentes:
- da maneira antiga, incluindo o núcleo do Jasmine e seus arquivos de teste usando uma tag
<script>
, - como uma ferramenta da CLI usando o Node.js,
- como uma biblioteca no Node.js,
- como parte de um sistema de build, como Gulp.js ou Grunt.js via grunt-contrib-jasmine e gulp-jasmine-browser
Você também pode usar o Jasmine para testar seu código em Python com jasmine-py, que pode ser instalado a partir do PyPI usando o comando pip install jasmine
. Este pacote contém um servidor para a web que serve e executa uma suíte do Jasmine para seu projeto e um script da CLI para executar testes e integrações contínuas.
O Jasmine também está disponível para projetos Ruby via jasmine-gem, que pode ser instalado adicionando gem 'jasmine'
ao seu Gemfile e executando bundle install
. Ele inclui um servidor para servir e executar testes, um script da CLI e também geradores para projetos Ruby on Rails.
Agora, vamos nos concentrar em como usar o Jasmine com JavaScript:
Usando o Jasmine Standalone
Comece baixando a versão mais recente do Jasmine na página de releases (lançamentos).

Em seguida, basta extrair o arquivo zip, de preferência dentro de uma pasta no projeto que você deseja testar.
A pasta conterá diversos arquivos e pastas padrão:
/src
: contém os arquivos de código-fonte que você deseja testar. Essa pasta pode ser excluída se você já tiver a pasta do seu projeto configurada. Ela também pode ser usada quando for apropriado para hospedar seu código-fonte.
/lib
: contém os arquivos principais do Jasmine.
/spec
: contém os testes que você vai escrever.
SpecRunner.html
: é um arquivo usado como um executor de testes. Você executa suas especificações simplesmente iniciando esse arquivo.
Este é o conteúdo de um arquivo SpecRunner.html
padrão:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v3.2.1</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-3.2.1/jasmine_favicon.png">
<link rel="stylesheet" href="lib/jasmine-3.2.1/jasmine.css">
<script src="lib/jasmine-3.2.1/jasmine.js"></script>
<script src="lib/jasmine-3.2.1/jasmine-html.js"></script>
<script src="lib/jasmine-3.2.1/boot.js"></script>
<!-- include source files here... -->
<script src="src/Player.js"></script>
<script src="src/Song.js"></script>
<!-- include spec files here... -->
<script src="spec/SpecHelper.js"></script>
<script src="spec/PlayerSpec.js"></script>
</head>
<body>
</body>
</html>
Lembre-se de que você precisa alterar os arquivos incluídos das pastas /src
e /spec
para conter seus arquivos de código-fonte e de teste reais.
Usando o Jasmine como uma biblioteca
Você também pode usar o Jasmine como uma biblioteca em seu projeto. Por exemplo, o código a seguir importa e executa o Jasmine:
var Jasmine = require('jasmine');
var jasmine = new Jasmine();
jasmine.loadConfigFile('spec/support/jasmine.json');
jasmine.execute();
Primeiro, solicitamos/importamos o Jasmine e usamos o método loadConfigFile()
para carregar o arquivo de configuração disponível no caminho spec/support/jasmine.json
e, finalmente, executamos o Jasmine.
Usando o Jasmine via CLI
Você também pode usar o Jasmine a partir da CLI (interface de linha de comando), o que permite executar facilmente os testes do Jasmine e, por padrão, gerar os resultados no terminal.
Seguiremos essa abordagem para executar nossos testes de exemplo neste guia, então, primeiro, execute o seguinte comando para instalar o Jasmine globalmente:
npm install -g jasmine
Você pode precisar executar sudo para instalar pacotes npm globalmente, dependendo da sua configuração do npm.
Agora, crie uma pasta para seu projeto e navegue para dentro dela:
$ mkdir jasmine-project $ cd jasmine-project
Em seguida, execute o seguinte comando para inicializar seu projeto para o Jasmine:
O comando simplesmente cria uma pasta spec
e um arquivo de configuração JSON. Esta é a saída do comando dir
:
.
└── spec
└── support
└── jasmine.json
2 directories, 1 file
Este é o conteúdo de um arquivo jasmine.json
padrão:
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
spec_dir
: especifica onde o Jasmine procura arquivos de teste.spec_files
: especifica os padrões de arquivos de teste. Por padrão, todos os arquivos JS que terminam com as strings Spec ou spec.helpers
: especifica onde o Jasmine procura arquivos auxiliares. Os arquivos auxiliares são executados antes das especificações e podem ser usados para definir matchers personalizados.stopSpecOnExpectationFailure
: quando definido comotrue
, interromperá imediatamente uma especificação na primeira falha de uma expectativa (pode ser usado como uma opção CLI via--stop-on-failure
).random
: quando definido comotrue
, o Jasmine executará os casos de teste de modo pseudoaleatório (pode ser usado como uma opção da CLI via--random
).
Os arrays spec_files
e helpers
também podem conter padrões Glob (graças ao pacote node-glob) para especificar caminhos de arquivo que são padrões que você normalmente usa para especificar um conjunto de arquivos ao trabalhar no Bash (por exemplo, ls *.js
).
Se você não usar o local padrão para o arquivo de configuração jasmine.json
, basta especificar o local personalizado por meio da opção jasmine --config
.
Você pode encontrar mais opções da CLI na documentação oficial.
Entendendo o Jasmine
Nesta seção, aprenderemos sobre os elementos básicos dos testes do Jasmine, como suítes, especificações, expectativas, matchers e spies etc.
Na pasta do projeto, ao executar o comando para inicializar um novo módulo do Node, npm init -y
, você criará um arquivo package.json
com informações padrão:
{
"name": "jasmine-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Em seguida, crie um arquivo index.js
e adicione o seguinte código:
function fibonacci(n){
if (n === 1) {
return [0, 1];
}
else {
var s = fibonacci(n - 1);
s.push(s[s.length - 1] + s[s.length - 2]);
return s;
}
}
function isPrime(num){
for (let i = 2; i < num; i++)
if (num % i === 0) return false;
return num !== 1 && num !== 0;
}
function isEven(n) {
return n % 2 == 0;
}
function isOdd(n) {
return Math.abs(n % 2) == 1;
}
function toLowerCase(str){
return str.toLowerCase();
}
function toUpperCase(str){
return str.toUpperCase();
}
function contains(str, substring, fromIndex){
return str.indexOf(substring, fromIndex) !== -1;
}
function repeat(str, n){
return (new Array(n + 1)).join(str);
}
module.exports = {
fibonacci: fibonacci,
isPrime: isPrime,
isEven: isEven,
isOdd: isOdd,
toLowerCase: toLowerCase,
toUpperCase: toUpperCase,
contains: contains,
repeat: repeat
};
Suítes
Uma suíte agrupa um conjunto de especificações ou casos de teste. É usada para testar um comportamento específico do código em JavaScript que geralmente é encapsulado por um objeto/classe ou uma função. Ela é criada usando a função global do Jasmine describe()
, que recebe dois parâmetros, o título da suíte de teste e uma função que implementa o código real da suíte de teste.
Vamos começar criando nossa primeira suíte de teste. Dentro da pasta spec
, crie um arquivo MyJSUtilitiesSpec.js
e adicione:
describe("MyJSUtilities", function() { /* ... */ });
MyJSUtilities é o nome da suíte de teste de nível superior.
Como agrupar e aninhar suítes
Para melhor organização e descrição precisa do nosso conjunto de testes, podemos aninhar suítes dentro da suíte de nível superior. Por exemplo, vamos adicionar duas suítes à suíte MyJSUtilities:
describe("String Utils", function() { /*...*/});describe("Math Utils", function() { /*...*/});
Dentro da suíte Math Utils, vamos também adicionar duas suítes aninhadas:
describe("Basic Math Utils", function() { /* ... */ }); describe("Advanced Math Utils", function() { /* ... */ });
Estamos agrupando testes relacionados em testes para String Utils, Basic Math Utils e Advanced Math Utils e aninhando-os dentro da suíte de nível superior MyJSUtilities. Isso comporá suas especificações como árvores semelhantes a uma estrutura de pastas.
A estrutura de aninhamento será mostrada no relatório, o que facilita a localização de testes com falha.
Como excluir suítes
Você pode desabilitar temporariamente uma suíte usando a função xdescribe()
. Ela tem a mesma assinatura (parâmetros) de uma função describe()
, o que significa que você pode desabilitar rapidamente suas suítes existentes simplesmente adicionando um x
à função.
As especificações dentro de uma função xdescribe()
serão marcadas como pendentes e não serão executadas no relatório.
Especificações
Uma especificação declara um caso de teste que pertence a uma suíte de teste. Isso é feito chamando a função global do Jasmine it()
, que recebe dois parâmetros: o título da especificação (que descreve a lógica que queremos testar) e uma função que implementa o caso de teste real.
Uma especificação pode conter uma ou mais expectativas. Cada expectativa é simplesmente uma asserção que pode retornar true
ou false
. Para que a especificação seja aprovada, todas as expectativas pertencentes à especificação devem ser true
. Caso contrário, a especificação falha.
Dentro da nossa suíte String Utils, adicione estas especificações:
describe("String Utils", function() { it("should be able to lower case a string",function() { /*...*/ }); it("should be able to upper case a string",function() { /*...*/ }); it("should be able to confirm if a string contains a substring",function() { /*...*/ }); it("should be able repeat a string multiple times",function() { /*...*/ });});
Dentro da nossa suíte Basic Math Utils, vamos adicionar algumas especificações:
describe("Basic Math Utils", function() { it("should be able to tell if a number is even",function() { /*...*/ }); it("should be able to tell if a number is odd",function() { /*...*/ }); });
Para Advanced Math Utils, vamos adicionar as especificações:
describe("Advanced Math Utils", function() { it("should be able to tell if a number is prime",function() { /*...*/ }); it("should be able to calculate the fibonacci of a number",function() { /*...*/ }); });
Como excluir especificações
Assim como as suítes, você também pode excluir especificações individuais usando a função xit()
, que desabilita temporariamente a especificação it()
e a marca como pendente.
Expectativas
As expectativas são criadas usando a função expect()
que recebe um valor chamado real (que pode ser valores, expressões, variáveis, funções, objetos etc.). As expectativas compõem a especificação e são usadas junto com funções matcher (via encadeamento) para definir o que o desenvolvedor espera que uma unidade específica de código execute.
Uma função matcher compara um valor real (passado para a função expect()
com a qual está encadeada) e um valor esperado (passado diretamente como um parâmetro para o matcher) e retorna true ou false, o que passa ou falha a especificação.
Você pode encadear a função expect()
com vários matchers. Para negar/inverter o resultado booleano de qualquer matcher, você pode usar a palavra-chave not
antes de chamar o matcher.
Vamos implementar as especificações do nosso exemplo. Por enquanto, usaremos expect()
com o matcher nothing()
, que faz parte dos matchers integrados que veremos um pouco mais tarde. Isso passará em todas as especificações, pois não esperamos nada neste momento.
describe("MyJSUtilities", function() {describe(">String Utils", function() { it("should be able to lower case a string",function() { expect().nothing(); }); it("should be able to upper case a string",function() { expect().nothing(); }); it("should be able to confirm if a string contains a substring",function() { expect().nothing(); }); it("should be able repeat a string multiple times",function() { expect().nothing(); }); });describe("Math Utils", function() { describe("Basic Math Utils", function() { it("should be able to tell if a number is even",function() { expect().nothing(); }); it("should be able to tell if a number is odd",function() { expect().nothing(); }); }); describe("Advanced Math Utils", function() { it("should be able to tell if a number is prime",function() { expect().nothing(); }); it("should be able to calculate the fibonacci of a number",function() { expect().nothing(); }); }); });});
Esta é uma captura de tela dos resultados neste ponto:

Você pode usar matchers integrados ou também criar seus próprios matchers personalizados para suas necessidades específicas.
Matchers integrados
O Jasmine fornece um rico conjunto de matchers integrados. Vamos ver alguns dos mais importantes:
toBe()
para testar identidade,toBeNull()
para testarnull
,toBeUndefined()/toBeDefined()
para testarundefined
/nãoundefined
,toBeNaN()
para testar NaN (Not A Number)toEqual()
para testar igualdade,toBeFalsy()/toBeTruthy()
para testar falsidade/veracidade etc.
Você pode encontrar a lista completa de matchers na documentação.
Vamos agora implementar nossas especificações com alguns desses matchers quando apropriado. Primeiro, importe as funções que estamos testando em nosso arquivo MyJSUtilitiesSpec.js
:
const utils = require("../index.js");
Em seguida, comece com a suíte String Utils e altere expect().nothing()
com as expectativas apropriadas.
Por exemplo, para a primeira especificação, esperamos que o método toLowerCase()
seja primeiro definido e, em segundo lugar, retorne uma string em minúsculas, ou seja:
it("should be able to lower case a string",function() { expect(utils.toLowerCase).toBeDefined(); expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world"); });
Este é o código completo para a suíte:
describe(">String Utils", function() { it("should be able to lower case a string",function() { expect(utils.toLowerCase).toBeDefined(); expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world"); }); it("should be able to upper case a string",function() { expect(utils.toUpperCase).toBeDefined(); expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD"); }); it("should be able to confirm if a string contains a substring",function() { expect(utils.contains).toBeDefined(); expect(utils.contains("hello world","hello",0)).toBeTruthy(); }); it("should be able repeat a string multiple times",function() { expect(utils.repeat).toBeDefined(); expect(utils.repeat("hello", 3)).toEqual("hellohellohello"); }); });
Matchers personalizados
O Jasmine fornece a capacidade de escrever matchers personalizados para implementar asserções não cobertas pelos matchers integrados ou apenas para tornar os testes mais descritivos e legíveis.
Por exemplo, vamos pegar a seguinte especificação:
it("should be able to tell if a number is even",function() { expect(utils.isEven).toBeDefined(); expect(utils.isEven(2)).toBeTruthy(); expect(utils.isEven(1)).toBeFalsy(); });
Vamos supor que o método isEven()
não esteja implementado. Se executarmos os testes, receberemos mensagens como a seguinte captura de tela:

A mensagem de falha que recebemos diz Expected undefined to be defined, o que não nos dá nenhuma pista do que está acontecendo. Então, vamos tornar essa mensagem mais significativa no contexto do nosso domínio de código (isso será mais útil para bases de código complexas). Para isso, vamos criar um matcher personalizado.
Criamos matchers personalizados usando o método addMatchers()
, que recebe um objeto composto por uma ou várias propriedades que serão adicionadas como matchers. Cada propriedade deve fornecer uma função factory que recebe dois parâmetros: util
, que possui um conjunto de funções utilitárias para os matchers usarem (consulte: MatchersUtil.js
) e customEqualityTesters
, que precisa ser passado se util.equals
for chamado, devendo retornar um objeto com uma função compare
, que será chamada para verificar a expectativa.
Precisamos registrar o matcher personalizado antes de executar cada especificação usando o método beforeEach()
:
describe("/Basic Math Utils", function () {beforeEach(function () {jasmine.addMatchers({hasEvenMethod: function (util, customEqualityTesters) {return {compare: function (actual, expected) {var result = { pass: utils.isEven !== undefined };if (result.pass) {result.message = "Expected isEven() to be not defined."}else {result.message = "Expected isEven() to be defined."}return result;}}}});});/*...*/});
Podemos então usar o matcher personalizado em vez de expect(utils.isEven).toBeDefined()
:
expect().hasEvenMethod();
Isso nos dará uma mensagem de falha melhor:

Usando beforeEach() e afterEach()
Para inicializar e limpar suas especificações, o Jasmine fornece duas funções globais, beforeEach()
e afterEach()
:
- A função
beforeEach
é chamada uma vez antes de cada especificação na suíte onde é chamada. - A função
afterEach
é chamada uma vez após cada especificação na suíte onde é chamada.
Por exemplo, se você precisar usar quaisquer variáveis em sua suíte de teste, pode simplesmente declará-las no início da função describe()
e colocar qualquer código de inicialização ou instanciação dentro de uma função beforeEach()
. Finalmente, você pode usar a função afterEach()
para redefinir as variáveis após cada especificação, para que você possa ter testes unitários puros sem a necessidade de repetir o código de inicialização e limpeza para cada especificação.
A função beforeEach()
também é perfeitamente combinada com muitas APIs do Jasmine, como o método addMatchers()
para criar matchers personalizados ou também com a função done()
para aguardar operações assíncronas antes de continuar o teste.
Falhando um teste
Você pode forçar um teste a falhar usando o método global fail()
disponível no Jasmine. Por exemplo:
it("should explicitly fail", function () { fail('Forced to fail'); });
Você deve receber o seguinte erro:

Testando exceções
Quando você está testando seu código unitariamente, erros e exceções podem ser lançados, então você pode precisar testar esses cenários. O Jasmine fornece os matchers toThrow()
e toThrowError()
para testar quando uma exceção é lançada ou para testar uma exceção específica, respectivamente.
Por exemplo, se tivermos uma função que lança uma exceção TypeError
:
function throwsError() { throw new TypeError("A type error"); }
Você pode escrever uma especificação para testar se uma exceção é lançada:
it('it should throw an exception', function () { expect(throwsError).toThrow(); });
Ou você também pode usar o teste para a exceção TypeError
específica:
it('it should throw a TypeError', function () { expect(throwsError).toThrowError(TypeError); });
Entendendo spies
Muitas vezes, os métodos dependem de outros métodos. Isso significa que, ao testar um método, você pode acabar testando suas dependências também. Isso não é recomendado em testes, ou seja, você precisa garantir que está testando a função pura, isolando o método e vendo como ele se comporta, dado um conjunto de entradas.
O Jasmine fornece spies que podem ser usados para "espionar"/ouvir chamadas de método em objetos e relatar se um método é chamado e em qual contexto e argumentos.
O Jasmine fornece duas maneiras de espionar chamadas de método: usando os métodos spyOn()
ou createSpy()
.
Você pode usar spyOn()
quando o método já existe no objeto. Caso contrário, você precisa usar jasmine.createSpy()
, que retorna uma nova função.
Por padrão, um spy apenas relatará se uma chamada foi feita sem chamar a função espionada (ou seja, a função parará de ser executada), mas você pode alterar o comportamento padrão usando estes métodos:
and.callThrough()
: chama a função original,and.returnValue(value)
: retorna o valor especificado,and.callFake(fn)
: chama a função falsa em vez da original,and.throwError(err)
: lança um erro,and.stub()
: redefine o comportamento de stub padrão.
Você pode usar um spy para coletar estatísticas de tempo de execução na função espionada, por exemplo, se você quiser saber quantas vezes sua função foi chamada.
Digamos que queremos garantir que nosso método toUpperCase()
esteja usando o método String.toUpperCase()
integrado. Precisamos, simplesmente, espionar String.toUpperCase()
usando:
it("should be able to upper case a string", function () {
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase')
expect(utils.toUpperCase).toBeDefined(); expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD"); expect(String.prototype.toUpperCase).toHaveBeenCalled(); expect(spytoUpperCase.calls.count()).toEqual(1); });

O teste falhou devido à segunda expectativa, pois utils.toUpperCase("hello world")
retornou undefined
em vez do HELLO WORLD esperado. Isso ocorre porque, como mencionamos, anteriormente, após criar o spy em toUpperCase()
, o método não é executado. Precisamos mudar esse comportamento padrão chamando callThrough()
:
Observe que uma funçãospy
substitui a função espionada por um stub por padrão. Se você precisar chamar a função original, pode adicionar.and.callThrough()
ao seu objetospy
.
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.callThrough();
Agora, todas as expectativas passam.
Você também pode usar and.callFake()
ou and.returnValue()
para falsificar a função espionada ou apenas o valor de retorno se você não quiser chamar a função real:
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.returnValue("HELLO WORLD");
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.callFake(function(){ return "HELLO WORLD"; });
Agora, se acabarmos não usando o String.toUpperCase()
integrado em nossa própria implementação de utils.toUpperCase()
, obteremos estas falhas:

As duas expectativas expect(String.prototype.toUpperCase).toHaveBeenCalled()
expect(spytoUpperCase.calls.count()).toEqual(1)
falharam.
Como lidar com assincronia no Jasmine
Se o código que você está testando contém operações assíncronas, você precisa de uma maneira de informar ao Jasmine quando as operações assíncronas forem concluídas.
Por padrão, o Jasmine espera que qualquer operação assíncrona, definida por uma callback, uma promise ou a palavra-chave async
, seja concluída. Se o Jasmine encontrar uma callback, promise ou palavra-chave async em uma dessas funções: beforeEach
, afterEach
, beforeAll
, afterAll
e it
, ele aguardará a conclusão da operação assíncrona antes de prosseguir para a próxima operação.
Usando done()
com beforeEach()
/it()
..
Vamos pegar nosso exemplo simulateAsyncOp()
, que simula uma operação assíncrona usando setTimeout()
. Em um cenário do mundo real, pode ser uma solicitação Ajax ou qualquer coisa semelhante que aconteça de maneira assíncrona:
function simulateAsyncOp(callback){
setTimeout(function () { callback(); }, 2000); }
Para testar esta função, podemos usar a função beforeEach()
com a callback especial done()
. Nosso código precisa invocar done()
para informar ao Jasmine que a operação assíncrona foi concluída:
describe("/Async Op", function () {var asyncOpCompleted = false;beforeEach(function (done) {utils.simulateAsyncOp(function(){ asyncOpCompleted = true; done();});});it("should be able to tell if the async call has completed", function () { expect(asyncOpCompleted).toEqual(true);});});
Podemos notar rapidamente uma desvantagem desse método, então precisamos escrever nosso código para aceitar a callback done()
. No nosso caso, não codificamos o método done()
em nosso simulateAsyncOp(fn)
, mas fornecemos um parâmetro de callback apenas para poder chamar done()
.
Usando as promises
Se você não quiser criar código que dependa de como você escreve seu teste, pode usar uma promise e chamar a callback done()
quando a promise for resolvida. Ou melhor ainda, a partir do Jasmine 2.7+, se seu código retornar uma Promise
, o Jasmine aguardará até que ela seja resolvida ou rejeitada antes de executar o próximo código.
Usando async/await
O Jasmine 2.7+ suporta chamadas a async
e await
em especificações. Isso o libera de colocar asserções em um bloco .then()
ou .catch()
.
it("should work with async/await", async () => { let completed = false; completed = await utils.simulateAsyncOp(); expect(completed).toEqual(true); });
Esta é a implementação de simulateAsyncOp
:
function simulateAsyncOp() {
return new Promise(resolve => { setTimeout(() => { resolve(true); }, 1000); }); }
Usando o Jasmine Clock
O Jasmine Clock é usado para testar código assíncrono que depende de funções de tempo, como setTimeout()
, da mesma maneira que testamos código síncrono, simulando APIs baseadas em tempo com métodos personalizados. Desse modo, você pode executar as funções testadas sincronicamente controlando ou avançando manualmente o relógio.
Você pode instalar o Jasmine Clock chamando a função jasmine.clock().install
em sua especificação ou suíte.
Depois de usar o relógio, você precisa desinstalá-lo para restaurar as funções originais.
Com o Jasmine Clock, você pode controlar as funções setTimeout
ou setInterval
do JavaScript avançando o relógio para avançar no tempo usando a função jasmine.clock().tick
, que recebe o número de milissegundos que você pode mover.
Você também pode usar o Jasmine Clock para simular a data atual.
beforeEach(function () {jasmine.clock().install();});afterEach(function() {jasmine.clock().uninstall();});it("should call the asynchronous operation synchronously", function() {var completed = false;utils.simulateAsyncOp(function(){completed = true;});expect(completed).toEqual(false);jasmine.clock().tick(1001);expect(completed).toEqual(true);});
Esta é a função simulateAsyncOp
:
function simulateAsyncOp(callback){
setTimeout(function () { callback(); }, 1000); }
Caso você não tenha especificado um horário para a função mockDate
, ela usará a data atual.
Lidando com erros
Se o seu código assíncrono falhar devido a algum erro, você vai querer que suas especificações falhem da maneira certa. A partir do Jasmine 2.6+, quaisquer erros não tratados são enviados para a especificação atualmente executada.
O Jasmine também fornece uma maneira que você pode usar se precisar falhar explicitamente em suas especificações:
- usando a callback
done()
combeforeEach()
chamando o métododone.fail(err)
, - simplesmente passando um erro para a callback
done(err)
(Jasmine 3+), - chamando o método
reject()
de umaPromise
.
Conclusão
Neste guia, apresentamos o Jasmine e vimos como começar a usá-lo para fazer testes unitários de seu código em JavaScript. Agradecemos pela leitura!
Este artigo foi publicado originalmente em techiediaries.