Expect
Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →
Ao escrever testes, você frequentemente precisa verificar se valores atendem a determinadas condições. O expect fornece acesso a diversos "matchers" que permitem validar diferentes aspectos.
Para matchers adicionais do Jest mantidos pela comunidade, confira jest-extended.
Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →
Os exemplos em TypeScript desta página só funcionarão conforme documentado se você importar explicitamente as APIs do Jest:
import {expect, jest, test} from '@jest/globals';
Consulte o guia Primeiros Passos para detalhes sobre como configurar o Jest com TypeScript.
Referência
- Expect
- Modificadores
- Comparadores
.toBe(value).toHaveBeenCalled().toHaveBeenCalledTimes(number).toHaveBeenCalledWith(arg1, arg2, ...).toHaveBeenLastCalledWith(arg1, arg2, ...).toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....).toHaveReturnedTimes(number).toHaveReturnedWith(value).toHaveLastReturnedWith(value).toHaveNthReturnedWith(nthCall, value).toHaveLength(number).toHaveProperty(keyPath, value?).toBeCloseTo(number, numDigits?).toBeDefined().toBeFalsy().toBeGreaterThan(number | bigint).toBeGreaterThanOrEqual(number | bigint).toBeLessThan(number | bigint).toBeLessThanOrEqual(number | bigint).toBeInstanceOf(Class).toBeNull().toBeTruthy().toBeUndefined().toBeNaN().toContain(item).toContainEqual(item).toEqual(value).toMatch(regexp | string).toMatchObject(object).toMatchSnapshot(propertyMatchers?, hint?).toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot).toStrictEqual(value).toThrow(error?).toThrowErrorMatchingSnapshot(hint?).toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
- Matchers Assimétricos
expect.anything()expect.any(constructor)expect.arrayContaining(array)expect.not.arrayContaining(array)expect.arrayOf(value)expect.not.arrayOf(value)expect.closeTo(number, numDigits?)expect.objectContaining(object)expect.not.objectContaining(object)expect.stringContaining(string)expect.not.stringContaining(string)expect.stringMatching(string | regexp)expect.not.stringMatching(string | regexp)
- Contagem de Asserções
- Utilitários de Extensão
- Propriedades serializáveis
Expect
expect(value)
A função expect é usada sempre que você deseja testar um valor. Raramente você chamará expect isoladamente. Em vez disso, você usará expect combinado com uma função "matcher" para verificar algo sobre um valor.
É mais fácil entender com um exemplo. Suponha que você tenha um método bestLaCroixFlavor() que deve retornar a string 'grapefruit'. Veja como testar isso:
test('the best flavor is grapefruit', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});
Neste caso, toBe é a função matcher. Existem diversos matchers diferentes, documentados abaixo, para ajudar a testar vários aspectos.
O argumento para expect deve ser o valor produzido pelo seu código, e qualquer argumento para o matcher deve ser o valor correto. Se você inverter essa ordem, os testes ainda funcionarão, mas as mensagens de erro em testes falhos parecerão estranhas.
Modificadores
.not
Se você sabe testar algo, .not permite testar seu oposto. Por exemplo, este código verifica que o melhor sabor da La Croix não é coco:
test('the best flavor is not coconut', () => {
expect(bestLaCroixFlavor()).not.toBe('coconut');
});
.resolves
Use resolves para extrair o valor de uma promise resolvida, permitindo encadear outros matchers. Se a promise for rejeitada, a asserção falha.
Por exemplo, este código testa se a promise é resolvida e se o valor resultante é 'lemon':
test('resolves to lemon', () => {
// make sure to add a return statement
return expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});
Como você ainda está testando promises, o teste permanece assíncrono. Portanto, será necessário informar ao Jest para aguardar retornando a asserção extraída.
Alternativamente, você pode usar async/await combinado com .resolves:
test('resolves to lemon', async () => {
await expect(Promise.resolve('lemon')).resolves.toBe('lemon');
await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus');
});
.rejects
Use .rejects para extrair o motivo de uma promise rejeitada, permitindo encadear outros matchers. Se a promise for resolvida, a asserção falha.
Por exemplo, este código testa se a promise é rejeitada com o motivo 'octopus':
test('rejects to octopus', () => {
// make sure to add a return statement
return expect(Promise.reject(new Error('octopus'))).rejects.toThrow(
'octopus',
);
});
Como você ainda está testando promises, o teste permanece assíncrono. Portanto, será necessário informar ao Jest para aguardar retornando a asserção extraída.
Alternativamente, você pode usar async/await combinado com .rejects.
test('rejects to octopus', async () => {
await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus');
});
Comparadores
.toBe(value)
Use .toBe para comparar valores primitivos ou verificar identidade referencial de instâncias de objetos. Ele chama Object.is para comparar valores, o que é ainda melhor para testes que o operador de igualdade estrita ===.
Por exemplo, este código validará algumas propriedades do objeto can:
const can = {
name: 'pamplemousse',
ounces: 12,
};
describe('the can', () => {
test('has 12 ounces', () => {
expect(can.ounces).toBe(12);
});
test('has a sophisticated name', () => {
expect(can.name).toBe('pamplemousse');
});
});
Não use .toBe com números de ponto flutuante. Por exemplo, devido a arredondamentos, em JavaScript 0.2 + 0.1 não é estritamente igual a 0.3. Se você tiver números de ponto flutuante, prefira .toBeCloseTo.
Embora o matcher .toBe verifique a identidade referencial, ele relata uma comparação profunda de valores se a asserção falhar. Se as diferenças entre propriedades não ajudarem você a entender por que um teste falhou, especialmente se o relatório for extenso, você pode mover a comparação para dentro da função expect. Por exemplo, para verificar se os elementos são a mesma instância ou não:
-
reescreva
expect(received).toBe(expected)comoexpect(Object.is(received, expected)).toBe(true) -
reescreva
expect(received).not.toBe(expected)comoexpect(Object.is(received, expected)).toBe(false)
.toHaveBeenCalled()
Use .toHaveBeenCalled para garantir que uma função simulada (mock) foi chamada.
Por exemplo, digamos que você tenha uma função drinkAll(drink, flavour) que recebe uma função drink e a aplica a todas as bebidas disponíveis. Você pode querer verificar se drink foi chamada. Você pode fazer isso com este conjunto de testes:
function drinkAll(callback, flavour) {
if (flavour !== 'octopus') {
callback(flavour);
}
}
describe('drinkAll', () => {
test('drinks something lemon-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'lemon');
expect(drink).toHaveBeenCalled();
});
test('does not drink something octopus-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'octopus');
expect(drink).not.toHaveBeenCalled();
});
});
.toHaveBeenCalledTimes(number)
Use .toHaveBeenCalledTimes para garantir que uma função mock foi chamada exatamente o número de vezes.
Por exemplo, digamos que você tenha uma função drinkEach(drink, Array<flavor>) que recebe uma função drink e a aplica a um array de bebidas passadas. Você pode querer verificar se a função drink foi chamada um número exato de vezes. Você pode fazer isso com este conjunto de testes:
test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});
.toHaveBeenCalledWith(arg1, arg2, ...)
Use .toHaveBeenCalledWith para garantir que uma função simulada foi chamada com argumentos específicos. Os argumentos são verificados com o mesmo algoritmo que .toEqual usa.
Por exemplo, digamos que você possa registrar uma bebida com a função register, e applyToAll(f) deve aplicar a função f a todas as bebidas registradas. Para garantir que isso funciona, você poderia escrever:
test('registration applies correctly to orange La Croix', () => {
const beverage = new LaCroix('orange');
register(beverage);
const f = jest.fn();
applyToAll(f);
expect(f).toHaveBeenCalledWith(beverage);
});
.toHaveBeenLastCalledWith(arg1, arg2, ...)
Se você tem uma função mock, pode usar .toHaveBeenLastCalledWith para testar com quais argumentos ela foi chamada por último. Por exemplo, digamos que você tenha uma função applyToAllFlavors(f) que aplica f a vários sabores, e você quer garantir que, quando você a chama, o último sabor que ela opera seja 'mango'. Você pode escrever:
test('applying to all flavors does mango last', () => {
const drink = jest.fn();
applyToAllFlavors(drink);
expect(drink).toHaveBeenLastCalledWith('mango');
});
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
Se você tem uma função mock, você pode usar .toHaveBeenNthCalledWith para testar com quais argumentos ela foi chamada na enésima vez. Por exemplo, digamos que você tenha uma função drinkEach(drink, Array<flavor>) que aplica f a um conjunto de sabores, e você quer garantir que, quando você a chamar, o primeiro sabor que ela opera seja 'lemon' e o segundo seja 'octopus'. Você pode escrever:
test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenNthCalledWith(1, 'lemon');
expect(drink).toHaveBeenNthCalledWith(2, 'octopus');
});
O argumento nth deve ser um número inteiro positivo começando em 1.
.toHaveReturned(): O argumento nth deve ser um número inteiro positivo começando em 1.
Se você tem uma função mock, você pode usar .toHaveReturned para testar que a função mock retornou com sucesso (ou seja, não lançou um erro) pelo menos uma vez. Por exemplo, digamos que você tenha um mock drink que retorna true. Você pode escrever:
test('drinks returns', () => {
const drink = jest.fn(() => true);
drink();
expect(drink).toHaveReturned();
});
.toHaveReturnedTimes(number)
Use .toHaveReturnedTimes para garantir que uma função simulada retornou com sucesso (ou seja, não lançou um erro) um número exato de vezes. Qualquer chamada à função simulada que lançar um erro não é contabilizada para o número de vezes que a função retornou.
Por exemplo, digamos que você tenha uma função simulada drink que retorna true. Você pode escrever:
test('drink returns twice', () => {
const drink = jest.fn(() => true);
drink();
drink();
expect(drink).toHaveReturnedTimes(2);
});
.toHaveReturnedWith(value)
Use .toHaveReturnedWith para garantir que uma função simulada retornou um valor específico.
Por exemplo, digamos que você tenha uma função simulada drink que retorna o nome da bebida consumida. Você pode escrever:
test('drink returns La Croix', () => {
const beverage = {name: 'La Croix'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage);
expect(drink).toHaveReturnedWith('La Croix');
});
.toHaveLastReturnedWith(value)
Use .toHaveLastReturnedWith para testar o valor específico que uma função simulada retornou na última chamada. Se a última chamada à função simulada lançou um erro, esse matcher falhará independentemente do valor fornecido como resultado esperado.
Por exemplo, digamos que você tenha uma função simulada drink que retorna o nome da bebida consumida. Você pode escrever:
test('drink returns La Croix (Orange) last', () => {
const beverage1 = {name: 'La Croix (Lemon)'};
const beverage2 = {name: 'La Croix (Orange)'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage1);
drink(beverage2);
expect(drink).toHaveLastReturnedWith('La Croix (Orange)');
});
.toHaveNthReturnedWith(nthCall, value)
Use .toHaveNthReturnedWith para testar o valor específico que uma função simulada retornou na enésima chamada. Se a enésima chamada à função simulada lançou um erro, esse matcher falhará independentemente do valor fornecido como resultado esperado.
Por exemplo, digamos que você tenha uma função simulada drink que retorna o nome da bebida consumida. Você pode escrever:
test('drink returns expected nth calls', () => {
const beverage1 = {name: 'La Croix (Lemon)'};
const beverage2 = {name: 'La Croix (Orange)'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage1);
drink(beverage2);
expect(drink).toHaveNthReturnedWith(1, 'La Croix (Lemon)');
expect(drink).toHaveNthReturnedWith(2, 'La Croix (Orange)');
});
O argumento nth deve ser um número inteiro positivo começando em 1.
.toHaveLength(number)
Use .toHaveLength para verificar se um objeto possui uma propriedade .length com um valor numérico específico.
Especialmente útil para verificar o tamanho de arrays ou strings.
expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);
.toHaveProperty(keyPath, value?)
Use .toHaveProperty para verificar se uma propriedade existe em um objeto no caminho keyPath fornecido. Para verificar propriedades profundamente aninhadas, você pode usar notação de ponto ou um array contendo o caminho completo.
Você pode fornecer um argumento opcional value para comparar recursivamente o valor da propriedade (igualdade profunda, como o matcher toEqual).
O exemplo abaixo contém um objeto houseForSale com propriedades aninhadas. Estamos usando toHaveProperty para verificar a existência e valores de várias propriedades:
// Object containing house features to be tested
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
'nice.oven': true,
},
livingroom: {
amenities: [
{
couch: [
['large', {dimensions: [20, 20]}],
['small', {dimensions: [10, 10]}],
],
},
],
},
'ceiling.height': 2,
};
test('this house has my desired features', () => {
// Example Referencing
expect(houseForSale).toHaveProperty('bath');
expect(houseForSale).toHaveProperty('bedrooms', 4);
expect(houseForSale).not.toHaveProperty('pool');
// Deep referencing using dot notation
expect(houseForSale).toHaveProperty('kitchen.area', 20);
expect(houseForSale).toHaveProperty('kitchen.amenities', [
'oven',
'stove',
'washer',
]);
expect(houseForSale).not.toHaveProperty('kitchen.open');
// Deep referencing using an array containing the keyPath
expect(houseForSale).toHaveProperty(['kitchen', 'area'], 20);
expect(houseForSale).toHaveProperty(
['kitchen', 'amenities'],
['oven', 'stove', 'washer'],
);
expect(houseForSale).toHaveProperty(['kitchen', 'amenities', 0], 'oven');
expect(houseForSale).toHaveProperty(
'livingroom.amenities[0].couch[0][1].dimensions[0]',
20,
);
expect(houseForSale).toHaveProperty(['kitchen', 'nice.oven']);
expect(houseForSale).not.toHaveProperty(['kitchen', 'open']);
// Referencing keys with dot in the key itself
expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall');
});
.toBeCloseTo(number, numDigits?)
Use toBeCloseTo para comparar números de ponto flutuante com igualdade aproximada.
O argumento opcional numDigits limita o número de dígitos verificados após o ponto decimal. Para o valor padrão 2, o critério é Math.abs(expected - received) < 0.005 (ou seja, 10 ** -2 / 2).
Comparações intuitivas frequentemente falham devido a erros de arredondamento na representação binária. Por exemplo, este teste falha:
test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBe(0.3); // Fails!
});
Falha porque em JavaScript 0.2 + 0.1 na verdade é 0.30000000000000004.
Por exemplo, este teste passa com precisão de 5 dígitos:
test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});
Como toBeCloseTo resolve especificamente erros de ponto flutuante, não suporta valores inteiros muito grandes.
.toBeDefined()
Use .toBeDefined para verificar se uma variável não é undefined. Por exemplo, para verificar se fetchNewFlavorIdea() retorna algum valor:
test('there is a new flavor idea', () => {
expect(fetchNewFlavorIdea()).toBeDefined();
});
Alternativamente, expect(fetchNewFlavorIdea()).not.toBe(undefined) funciona, mas é melhor evitar referências diretas a undefined.
.toBeFalsy()
Use .toBeFalsy quando não importa o valor específico, mas você precisa garantir que um valor seja falso em contexto booleano. Por exemplo:
drinkSomeLaCroix();
if (!getErrors()) {
drinkMoreLaCroix();
}
Você pode não se importar com o que getErrors retorna especificamente - pode ser false, null ou 0, e seu código ainda funcionaria. Então, se você quiser testar que não há erros após beber um pouco de La Croix, poderia escrever:
test('drinking La Croix does not lead to errors', () => {
drinkSomeLaCroix();
expect(getErrors()).toBeFalsy();
});
Em JavaScript, existem seis valores falsy: false, 0, '', null, undefined e NaN. Todo o resto é truthy.
.toBeGreaterThan(number | bigint)
Use toBeGreaterThan para comparar received > expected em valores numéricos ou big integer. Por exemplo, teste que ouncesPerCan() retorna um valor maior que 10 onças:
test('ounces per can is more than 10', () => {
expect(ouncesPerCan()).toBeGreaterThan(10);
});
.toBeGreaterThanOrEqual(number | bigint)
Use toBeGreaterThanOrEqual para comparar received >= expected em valores numéricos ou big integer. Por exemplo, teste que ouncesPerCan() retorna um valor de pelo menos 12 onças:
test('ounces per can is at least 12', () => {
expect(ouncesPerCan()).toBeGreaterThanOrEqual(12);
});
.toBeLessThan(number | bigint)
Use toBeLessThan para comparar received < expected em valores numéricos ou big integer. Por exemplo, teste que ouncesPerCan() retorna um valor menor que 20 onças:
test('ounces per can is less than 20', () => {
expect(ouncesPerCan()).toBeLessThan(20);
});
.toBeLessThanOrEqual(number | bigint)
Use toBeLessThanOrEqual para comparar received <= expected em valores numéricos ou big integer. Por exemplo, teste que ouncesPerCan() retorna um valor de no máximo 12 onças:
test('ounces per can is at most 12', () => {
expect(ouncesPerCan()).toBeLessThanOrEqual(12);
});
.toBeInstanceOf(Class)
Use .toBeInstanceOf(Class) para verificar se um objeto é uma instância de uma classe. Esse matcher usa instanceof internamente.
class A {}
expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws
.toBeNull()
.toBeNull() é equivalente a .toBe(null), mas as mensagens de erro são mais amigáveis. Portanto, use .toBeNull() quando quiser verificar se algo é null.
function bloop() {
return null;
}
test('bloop returns null', () => {
expect(bloop()).toBeNull();
});
.toBeTruthy()
Use .toBeTruthy quando você não se importa com o valor específico, mas quer garantir que ele seja verdadeiro em um contexto booleano. Por exemplo, digamos que você tenha um código como:
drinkSomeLaCroix();
if (thirstInfo()) {
drinkMoreLaCroix();
}
Você pode não se importar com o que thirstInfo retorna especificamente - pode ser true ou um objeto complexo, e seu código ainda funcionaria. Então, se quiser testar que thirstInfo será truthy após beber um pouco de La Croix, você poderia escrever:
test('drinking La Croix leads to having thirst info', () => {
drinkSomeLaCroix();
expect(thirstInfo()).toBeTruthy();
});
Em JavaScript, existem seis valores falsy: false, 0, '', null, undefined e NaN. Todo o resto é truthy.
.toBeUndefined()
Use .toBeUndefined para verificar se uma variável é undefined. Por exemplo, se quiser verificar que a função bestDrinkForFlavor(flavor) retorna undefined para o sabor 'octopus', porque não existe uma boa bebida com sabor de polvo:
test('the best drink for octopus flavor is undefined', () => {
expect(bestDrinkForFlavor('octopus')).toBeUndefined();
});
Você poderia escrever expect(bestDrinkForFlavor('octopus')).toBe(undefined), mas é melhor evitar referenciar undefined diretamente no seu código.
.toBeNaN()
Use .toBeNaN ao verificar se um valor é NaN.
test('passes when value is NaN', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});
.toContain(item)
Use .toContain quando quiser verificar se um item está em um array. Para testar os itens do array, isso usa ===, uma comparação de igualdade estrita. .toContain também pode verificar se uma string é uma substring de outra string.
Por exemplo, se getAllFlavors() retornar um array de sabores e você quiser ter certeza que lime está incluído, pode escrever:
test('the flavor list contains lime', () => {
expect(getAllFlavors()).toContain('lime');
});
Este matcher também aceita outros iteráveis como strings, sets, node lists e HTML collections.
.toContainEqual(item)
Use .toContainEqual quando você deseja verificar se um item com uma estrutura e valores específicos está contido em um array. Para testar os itens do array, esse matcher verifica recursivamente a igualdade de todos os campos, em vez de verificar a identidade do objeto.
describe('my beverage', () => {
test('is delicious and not sour', () => {
const myBeverage = {delicious: true, sour: false};
expect(myBeverages()).toContainEqual(myBeverage);
});
});
.toEqual(value)
Use .toEqual para comparar recursivamente todas as propriedades de instâncias de objetos (conhecida como igualdade "profunda"). Ele chama Object.is para comparar valores primitivos, que é ainda melhor para testes do que o operador de igualdade estrita ===.
Por exemplo, .toEqual e .toBe se comportam de maneira diferente neste conjunto de testes, fazendo com que todos os testes passem:
const can1 = {
flavor: 'grapefruit',
ounces: 12,
};
const can2 = {
flavor: 'grapefruit',
ounces: 12,
};
describe('the La Croix cans on my desk', () => {
test('have all the same properties', () => {
expect(can1).toEqual(can2);
});
test('are not the exact same can', () => {
expect(can1).not.toBe(can2);
});
});
toEqual ignora chaves de objetos com propriedades undefined, itens de array undefined, esparsidade de arrays ou incompatibilidade de tipo de objeto. Para considerar esses casos, use .toStrictEqual.
.toEqual não realiza uma verificação de igualdade profunda para dois erros. Apenas a propriedade message de um Error é considerada para igualdade. Recomenda-se usar o matcher .toThrow para testar erros.
Se as diferenças entre propriedades não ajudarem a entender por que um teste falha, especialmente em relatórios extensos, mova a comparação para dentro da função expect. Por exemplo, use o método equals da classe Buffer para verificar se os buffers contêm o mesmo conteúdo:
-
reescreva
expect(received).toEqual(expected)comoexpect(received.equals(expected)).toBe(true) -
reescreva
expect(received).not.toEqual(expected)comoexpect(received.equals(expected)).toBe(false)
.toMatch(regexp | string)
Use .toMatch para verificar se uma string corresponde a uma expressão regular.
Por exemplo, você pode não saber exatamente o que essayOnTheBestFlavor() retorna, mas sabe que é uma string muito longa e que a substring grapefruit deve estar em algum lugar. Você pode testar assim:
describe('an essay on the best flavor', () => {
test('mentions grapefruit', () => {
expect(essayOnTheBestFlavor()).toMatch(/grapefruit/);
expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit'));
});
});
Este matcher também aceita uma string, que tentará corresponder:
describe('grapefruits are healthy', () => {
test('grapefruits are a fruit', () => {
expect('grapefruits').toMatch('fruit');
});
});
.toMatchObject(object)
Use .toMatchObject para verificar se um objeto JavaScript corresponde a um subconjunto das propriedades de outro objeto. Ele corresponderá a objetos recebidos com propriedades que não estão no objeto esperado.
Você também pode passar um array de objetos, caso em que o método retornará true apenas se cada objeto no array recebido corresponder (no sentido do toMatchObject descrito acima) ao objeto correspondente no array esperado. Isso é útil para verificar se dois arrays correspondem no número de elementos, diferentemente do arrayContaining, que permite elementos extras no array recebido.
Você pode comparar propriedades contra valores ou contra outros matchers.
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
},
};
const desiredHouse = {
bath: true,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
wallColor: expect.stringMatching(/white|yellow/),
},
};
test('the house has my desired features', () => {
expect(houseForSale).toMatchObject(desiredHouse);
});
describe('toMatchObject applied to arrays', () => {
test('the number of elements must match exactly', () => {
expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]);
});
test('.toMatchObject is called for each elements, so extra object properties are okay', () => {
expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([
{foo: 'bar'},
{baz: 1},
]);
});
});
.toMatchSnapshot(propertyMatchers?, hint?)
Isso garante que um valor corresponda ao snapshot mais recente. Consulte o guia de Testes com Snapshot para mais informações.
Você pode fornecer um argumento opcional propertyMatchers, que é um objeto com matchers assimétricos como valores de um subconjunto de propriedades esperadas, apenas se o valor recebido for uma instância de objeto. Funciona como toMatchObject com critérios flexíveis para um subconjunto de propriedades, seguido por um teste de snapshot com critérios exatos para as demais propriedades.
Você pode fornecer um argumento opcional hint (string) que é anexado ao nome do teste. Embora o Jest sempre adicione um número ao final do nome do snapshot, dicas curtas e descritivas podem ser mais úteis que números para diferenciar múltiplos snapshots em um único bloco it ou test. O Jest ordena snapshots por nome no arquivo .snap correspondente.
.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)
Garante que um valor corresponda ao snapshot mais recente.
Você pode fornecer um argumento opcional propertyMatchers, que é um objeto com matchers assimétricos como valores de um subconjunto de propriedades esperadas, apenas se o valor recebido for uma instância de objeto. Funciona como toMatchObject com critérios flexíveis para um subconjunto de propriedades, seguido por um teste de snapshot com critérios exatos para as demais propriedades.
O Jest adiciona o argumento string inlineSnapshot ao matcher no arquivo de teste (em vez de um arquivo externo .snap) na primeira execução do teste.
Confira a seção sobre Snapshots em Linha para mais informações.
.toStrictEqual(value)
Use .toStrictEqual para testar se objetos têm a mesma estrutura e tipo.
Diferenças em relação ao .toEqual:
-
propriedades com valor
undefinedsão verificadas, ex:{a: undefined, b: 2}não será igual a{b: 2}; -
itens
undefinedsão considerados, ex:[2]não será igual a[2, undefined]; -
esparsidade de arrays é verificada, ex:
[, 1]não será igual a[undefined, 1]; -
tipos de objetos são verificados, ex: uma instância de classe com campos
aebnão será igual a um objeto literal com camposaeb.
class LaCroix {
constructor(flavor) {
this.flavor = flavor;
}
}
describe('the La Croix cans on my desk', () => {
test('are not semantically the same', () => {
expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'});
expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'});
});
});
.toThrow(error?)
Use .toThrow para testar se uma função lança um erro ao ser chamada. Por exemplo, para testar que drinkFlavor('octopus') lança um erro porque o sabor de polvo é muito desagradável:
test('throws on octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow();
});
Você deve envolver o código em uma função, caso contrário o erro não será capturado e a asserção falhará.
Você pode fornecer um argumento opcional para testar um erro específico:
-
expressão regular: a mensagem de erro corresponde ao padrão
-
string: a mensagem de erro inclui a substring
-
objeto de erro: a mensagem de erro é igual à propriedade message do objeto
-
classe de erro: o objeto de erro é instância da classe
Por exemplo, supondo que drinkFlavor esteja implementada assim:
function drinkFlavor(flavor) {
if (flavor === 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}
Podemos testar esse erro de várias formas:
test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}
// Test that the error message says "yuck" somewhere: these are equivalent
expect(drinkOctopus).toThrow(/yuck/);
expect(drinkOctopus).toThrow('yuck');
// Test the exact error message
expect(drinkOctopus).toThrow(/^yuck, octopus flavor$/);
expect(drinkOctopus).toThrow(new Error('yuck, octopus flavor'));
// Test that we get a DisgustingFlavorError
expect(drinkOctopus).toThrow(DisgustingFlavorError);
});
.toThrowErrorMatchingSnapshot(hint?)
Use .toThrowErrorMatchingSnapshot para testar se uma função lança um erro que corresponde ao snapshot mais recente.
Você pode fornecer um argumento opcional hint (string) que é anexado ao nome do teste. Embora o Jest sempre adicione um número ao final do nome do snapshot, dicas curtas e descritivas podem ser mais úteis que números para diferenciar múltiplos snapshots em um único bloco it ou test. O Jest ordena snapshots por nome no arquivo .snap correspondente.
Por exemplo, supondo que a função drinkFlavor lance um erro sempre que o sabor for 'octopus':
function drinkFlavor(flavor) {
if (flavor === 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}
O teste para essa função ficaria assim:
test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}
expect(drinkOctopus).toThrowErrorMatchingSnapshot();
});
E geraria o seguinte snapshot:
exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`;
Veja Testes de Snapshot em Árvores do React para mais informações sobre snapshot testing.
.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
Use .toThrowErrorMatchingInlineSnapshot para testar se uma função lança um erro que corresponde ao snapshot mais recente.
O Jest adiciona o argumento string inlineSnapshot ao matcher no arquivo de teste (em vez de um arquivo externo .snap) na primeira execução do teste.
Confira a seção sobre Snapshots em Linha para mais informações.
Matchers Assimétricos
expect.anything()
expect.anything() corresponde a qualquer valor exceto null ou undefined. Use dentro de toEqual ou toHaveBeenCalledWith no lugar de um valor literal. Por exemplo, para verificar se uma função mock foi chamada com um argumento não nulo:
test('map calls its argument with a non-null argument', () => {
const mock = jest.fn();
[1].map(x => mock(x));
expect(mock).toHaveBeenCalledWith(expect.anything());
});
expect.any(constructor)
expect.any(constructor) corresponde a qualquer instância criada com o construtor fornecido ou primitivos do tipo especificado. Use dentro de toEqual ou toHaveBeenCalledWith no lugar de um valor literal. Por exemplo, para verificar se uma função mock foi chamada com um número:
class Cat {}
function getCat(fn) {
return fn(new Cat());
}
test('randocall calls its callback with a class instance', () => {
const mock = jest.fn();
getCat(mock);
expect(mock).toHaveBeenCalledWith(expect.any(Cat));
});
function randocall(fn) {
return fn(Math.floor(Math.random() * 6 + 1));
}
test('randocall calls its callback with a number', () => {
const mock = jest.fn();
randocall(mock);
expect(mock).toHaveBeenCalledWith(expect.any(Number));
});
expect.arrayContaining(array)
expect.arrayContaining(array) corresponde a um array recebido que contém todos os elementos do array esperado. Ou seja, o array esperado é um subconjunto do array recebido. Portanto, também corresponde se o array recebido contiver elementos adicionais não presentes no esperado.
Você pode usá-lo no lugar de um valor literal:
-
em
toEqualoutoHaveBeenCalledWith -
para corresponder a uma propriedade em
objectContainingoutoMatchObject
describe('arrayContaining', () => {
const expected = ['Alice', 'Bob'];
it('matches even if received contains additional elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
});
it('does not match if received does not contain expected elements', () => {
expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected));
});
});
describe('Beware of a misunderstanding! A sequence of dice rolls', () => {
const expected = [1, 2, 3, 4, 5, 6];
it('matches even with an unexpected number 7', () => {
expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(
expect.arrayContaining(expected),
);
});
it('does not match without an expected number 2', () => {
expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual(
expect.arrayContaining(expected),
);
});
});
expect.not.arrayContaining(array)
expect.not.arrayContaining(array) corresponde a um array recebido que não contém todos os elementos do array esperado. Ou seja, o array esperado não é subconjunto do recebido.
É o inverso de expect.arrayContaining.
describe('not.arrayContaining', () => {
const expected = ['Samantha'];
it('matches if the actual array does not contain the expected elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(
expect.not.arrayContaining(expected),
);
});
});
expect.arrayOf(value)
expect.arrayOf(value) corresponde a um array recebido onde todos os elementos atendem ao matcher fornecido. Útil para validar que cada item satisfaz uma condição ou tipo específico.
Exemplo:
test('all elements in array are strings', () => {
expect(['apple', 'banana', 'cherry']).toEqual(
expect.arrayOf(expect.any(String)),
);
});
Este matcher é particularmente útil para validar arrays com estruturas complexas:
test('array of objects with specific properties', () => {
expect([
{id: 1, name: 'Alice'},
{id: 2, name: 'Bob'},
]).toEqual(
expect.arrayOf(
expect.objectContaining({
id: expect.any(Number),
name: expect.any(String),
}),
),
);
});
expect.not.arrayOf(value)
expect.not.arrayOf(value) corresponde a um array recebido onde nem todos os elementos atendem ao matcher fornecido.
Exemplo:
test('not all elements in array are strings', () => {
expect(['apple', 123, 'cherry']).toEqual(
expect.not.arrayOf(expect.any(String)),
);
});
expect.closeTo(number, numDigits?)
expect.closeTo(number, numDigits?) é útil para comparar números de ponto flutuante em propriedades de objetos ou itens de array. Para comparar números diretamente, prefira .toBeCloseTo.
O argumento opcional numDigits define a precisão decimal (padrão: 2 dígitos). Com valor padrão, o critério é Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2).
Por exemplo, este teste passa com precisão de 5 dígitos:
test('compare float in object properties', () => {
expect({
title: '0.1 + 0.2',
sum: 0.1 + 0.2,
}).toEqual({
title: '0.1 + 0.2',
sum: expect.closeTo(0.3, 5),
});
});
expect.objectContaining(object)
expect.objectContaining(object) corresponde a qualquer objeto recebido que contenha recursivamente as propriedades esperadas. Ou seja, o objeto esperado é um subconjunto do recebido. Portanto, também corresponde se o objeto recebido tiver propriedades adicionais.
Em vez de valores literais, você pode usar outros matchers como expect.anything() nas propriedades do objeto esperado.
Por exemplo, para verificar se uma função onPress foi chamada com um objeto Event contendo as propriedades event.x e event.y. Podemos fazer isso com:
test('onPress gets called with the right thing', () => {
const onPress = jest.fn();
simulatePresses(onPress);
expect(onPress).toHaveBeenCalledWith(
expect.objectContaining({
x: expect.any(Number),
y: expect.any(Number),
}),
);
});
expect.not.objectContaining(object)
expect.not.objectContaining(object) corresponde a qualquer objeto recebido que não contém recursivamente as propriedades esperadas. Ou seja, o objeto esperado não é subconjunto do recebido.
É o inverso de expect.objectContaining.
describe('not.objectContaining', () => {
const expected = {foo: 'bar'};
it('matches if the actual object does not contain expected key: value pairs', () => {
expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected));
});
});
expect.stringContaining(string)
expect.stringContaining(string) verifica se o valor recebido é uma string que contém exatamente a string esperada.
expect.not.stringContaining(string)
expect.not.stringContaining(string) verifica se o valor recebido não é uma string ou se é uma string que não contém exatamente a string esperada.
É o inverso de expect.stringContaining.
describe('not.stringContaining', () => {
const expected = 'Hello world!';
it('matches if the received value does not contain the expected substring', () => {
expect('How are you?').toEqual(expect.not.stringContaining(expected));
});
});
expect.stringMatching(string | regexp)
expect.stringMatching(string | regexp) verifica se o valor recebido é uma string que corresponde à string ou expressão regular esperada.
Você pode usá-lo no lugar de um valor literal:
-
em
toEqualoutoHaveBeenCalledWith -
para corresponder a um elemento em
arrayContaining -
para corresponder a uma propriedade em
objectContainingoutoMatchObject
Este exemplo também mostra como você pode aninhar vários matchers assimétricos, com expect.stringMatching dentro de expect.arrayContaining.
describe('stringMatching in arrayContaining', () => {
const expected = [
expect.stringMatching(/^Alic/),
expect.stringMatching(/^[BR]ob/),
];
it('matches even if received contains additional elements', () => {
expect(['Alicia', 'Roberto', 'Evelina']).toEqual(
expect.arrayContaining(expected),
);
});
it('does not match if received does not contain expected elements', () => {
expect(['Roberto', 'Evelina']).not.toEqual(
expect.arrayContaining(expected),
);
});
});
expect.not.stringMatching(string | regexp)
expect.not.stringMatching(string | regexp) verifica se o valor recebido não é uma string ou se é uma string que não corresponde à string ou expressão regular esperada.
É o inverso de expect.stringMatching.
describe('not.stringMatching', () => {
const expected = /Hello world!/;
it('matches if the received value does not match the expected regex', () => {
expect('How are you?').toEqual(expect.not.stringMatching(expected));
});
});
Contagem de Asserções
expect.assertions(number)
expect.assertions(number) verifica se um certo número de asserções é chamado durante um teste. Isso é frequentemente útil ao testar código assíncrono, para garantir que asserções em um callback realmente sejam executadas.
Por exemplo, digamos que temos uma função doAsync que recebe dois callbacks callback1 e callback2, e os chama assincronamente em uma ordem desconhecida. Podemos testar isso com:
test('doAsync calls both callbacks', () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
doAsync(callback1, callback2);
});
A chamada expect.assertions(2) garante que ambos os callbacks sejam realmente executados.
expect.hasAssertions()
expect.hasAssertions() verifica se pelo menos uma asserção é chamada durante um teste. Isso é frequentemente útil ao testar código assíncrono, para garantir que asserções em um callback realmente sejam executadas.
Por exemplo, digamos que temos algumas funções que lidam com estado. prepareState chama um callback com um objeto de estado, validateState executa nesse objeto de estado, e waitOnState retorna uma promise que aguarda até que todos os callbacks do prepareState sejam concluídos. Podemos testar isso com:
test('prepareState prepares a valid state', () => {
expect.hasAssertions();
prepareState(state => {
expect(validateState(state)).toBeTruthy();
});
return waitOnState();
});
A chamada expect.hasAssertions() garante que o callback do prepareState realmente seja executado.
Utilitários de Extensão
expect.addEqualityTesters(testers)
Você pode usar expect.addEqualityTesters para adicionar seus próprios métodos para testar se dois objetos são iguais. Por exemplo, suponha que você tenha uma classe em seu código que representa volume e pode determinar se dois volumes usando unidades diferentes são iguais. Você pode querer que toEqual (e outros matchers de igualdade) usem esse método personalizado ao comparar classes Volume. Você pode adicionar um testador de igualdade personalizado para fazer toEqual detectar e aplicar lógica personalizada ao comparar classes Volume:
- JavaScript
- TypeScript
// For simplicity in this example, we'll just support the units 'L' and 'mL'
export class Volume {
constructor(amount, unit) {
this.amount = amount;
this.unit = unit;
}
toString() {
return `[Volume ${this.amount}${this.unit}]`;
}
equals(other) {
if (this.unit === other.unit) {
return this.amount === other.amount;
} else if (this.unit === 'L' && other.unit === 'mL') {
return this.amount * 1000 === other.unit;
} else {
return this.amount === other.unit * 1000;
}
}
}
import {expect} from '@jest/globals';
import {Volume} from './Volume.js';
function areVolumesEqual(a, b) {
const isAVolume = a instanceof Volume;
const isBVolume = b instanceof Volume;
if (isAVolume && isBVolume) {
return a.equals(b);
} else if (isAVolume === isBVolume) {
return undefined;
} else {
return false;
}
}
expect.addEqualityTesters([areVolumesEqual]);
import {expect, test} from '@jest/globals';
import {Volume} from '../Volume.js';
import '../areVolumesEqual.js';
test('are equal with different units', () => {
expect(new Volume(1, 'L')).toEqual(new Volume(1000, 'mL'));
});
// For simplicity in this example, we'll just support the units 'L' and 'mL'
export class Volume {
public amount: number;
public unit: 'L' | 'mL';
constructor(amount: number, unit: 'L' | 'mL') {
this.amount = amount;
this.unit = unit;
}
toString(): string {
return `[Volume ${this.amount}${this.unit}]`;
}
equals(other: Volume): boolean {
if (this.unit === other.unit) {
return this.amount === other.amount;
} else if (this.unit === 'L' && other.unit === 'mL') {
return this.amount * 1000 === other.amount;
} else {
return this.amount === other.amount * 1000;
}
}
}
import {expect} from '@jest/globals';
import {Volume} from './Volume.js';
function areVolumesEqual(a: unknown, b: unknown): boolean | undefined {
const isAVolume = a instanceof Volume;
const isBVolume = b instanceof Volume;
if (isAVolume && isBVolume) {
return a.equals(b);
} else if (isAVolume === isBVolume) {
return undefined;
} else {
return false;
}
}
expect.addEqualityTesters([areVolumesEqual]);
import {expect, test} from '@jest/globals';
import {Volume} from '../Volume.js';
import '../areVolumesEqual.js';
test('are equal with different units', () => {
expect(new Volume(1, 'L')).toEqual(new Volume(1000, 'mL'));
});
API de testadores de igualdade personalizados
Testadores personalizados são funções que retornam o resultado (true ou false) da comparação de igualdade entre dois argumentos ou undefined se o testador não manipular os objetos fornecidos e quiser delegar a igualdade a outros testadores (por exemplo, os testadores de igualdade integrados).
Testadores personalizados são chamados com 3 argumentos: os dois objetos para comparar e o array de testadores personalizados (usado para testadores recursivos, veja a seção abaixo).
Estas funções auxiliares e propriedades podem ser encontradas em this dentro de um testador personalizado:
this.equals(a, b, customTesters?)
Esta é uma função de igualdade profunda que retornará true se dois objetos tiverem os mesmos valores (recursivamente). Ela aceita opcionalmente uma lista de testadores de igualdade personalizados para aplicar nas verificações de igualdade profunda. Ao usar esta função, repasse os testadores personalizados que seu tester recebeu para que verificações de igualdade subsequentes que equals aplica também possam usar testadores personalizados que o autor do teste possa ter configurado. Veja o exemplo na seção Testadores de igualdade personalizados recursivos para mais detalhes.
Matchers vs Testers
Matchers são métodos disponíveis no expect, por exemplo expect().toEqual(). toEqual é um matcher. Um tester é um método usado por matchers que realizam verificações de igualdade para determinar se objetos são iguais.
Matchers personalizados são úteis quando você deseja fornecer uma asserção customizada que autores de testes podem usar em seus testes. Por exemplo, o exemplo toBeWithinRange na seção expect.extend é um bom caso de matcher personalizado. Às vezes um autor de teste pode querer afirmar que dois números são exatamente iguais e deve usar toBe. Outras vezes, porém, pode desejar flexibilidade no teste, sendo toBeWithinRange uma asserção mais adequada.
Testadores de igualdade personalizados são ideais para estender globalmente os matchers do Jest aplicando lógica de igualdade customizada em todas as comparações. Autores de testes não podem ativar testadores personalizados para certas asserções e desativá-los para outras (nesse caso deve-se usar um matcher personalizado). Por exemplo, definir como verificar se dois objetos Volume são iguais para todos os matchers seria um bom uso de testador de igualdade personalizado.
Testadores de igualdade personalizados recursivos
Se seus testadores de igualdade personalizados verificam objetos com propriedades que requerem igualdade profunda, use o auxiliar this.equals disponível para testadores de igualdade. Este método equals é o mesmo que o Jest usa internamente para comparações profundas. Ele invoca seu testador personalizado e aceita um array de testadores personalizados como terceiro argumento. Testadores personalizados também recebem um array de testers como terceiro argumento - repasse-o para o terceiro argumento de equals para que verificações profundas subsequentes também aproveitem testadores personalizados.
Por exemplo, suponha uma classe Book contendo um array de classes Author, ambas com testadores personalizados. O testador de Book precisaria fazer uma verificação profunda no array de Authors, repassando os testadores recebidos para que o testador personalizado de Authors seja aplicado:
function areAuthorEqual(a, b) {
const isAAuthor = a instanceof Author;
const isBAuthor = b instanceof Author;
if (isAAuthor && isBAuthor) {
// Authors are equal if they have the same name
return a.name === b.name;
} else if (isAAuthor === isBAuthor) {
return undefined;
} else {
return false;
}
}
function areBooksEqual(a, b, customTesters) {
const isABook = a instanceof Book;
const isBBook = b instanceof Book;
if (isABook && isBBook) {
// Books are the same if they have the same name and author array. We need
// to pass customTesters to equals here so the Author custom tester will be
// used when comparing Authors
return (
a.name === b.name && this.equals(a.authors, b.authors, customTesters)
);
} else if (isABook === isBBook) {
return undefined;
} else {
return false;
}
}
expect.addEqualityTesters([areAuthorsEqual, areBooksEqual]);
Lembre-se de definir seus testadores de igualdade como funções regulares (não arrow functions) para acessar auxiliares de contexto como this.equals.
expect.addSnapshotSerializer(serializer)
Você pode chamar expect.addSnapshotSerializer para adicionar um módulo que formata estruturas de dados específicas da aplicação.
Em um arquivo de teste individual, um módulo adicionado precede quaisquer módulos da configuração snapshotSerializers, que por sua vez precedem os serializadores padrão para tipos JavaScript nativos e elementos React. O último módulo adicionado é o primeiro testado.
import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);
// affects expect(value).toMatchSnapshot() assertions in the test file
Adicionar um serializador de snapshot em arquivos de teste individuais (em vez da configuração snapshotSerializers):
-
Torna a dependência explícita em vez de implícita
-
Evita limitações de configuração que poderiam forçar eject do create-react-app
Veja configurando o Jest para mais informações.
expect.extend(matchers)
Use expect.extend para adicionar seus próprios matchers ao Jest. Por exemplo, suponha que você esteja testando uma biblioteca numérica e precise frequentemente verificar se números estão dentro de intervalos específicos. Você poderia abstrair isso em um matcher toBeWithinRange:
- JavaScript
- TypeScript
import {expect} from '@jest/globals';
function toBeWithinRange(actual, floor, ceiling) {
if (
typeof actual !== 'number' ||
typeof floor !== 'number' ||
typeof ceiling !== 'number'
) {
throw new TypeError('These must be of type number!');
}
const pass = actual >= floor && actual <= ceiling;
if (pass) {
return {
message: () =>
`expected ${this.utils.printReceived(
actual,
)} not to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${this.utils.printReceived(
actual,
)} to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: false,
};
}
}
expect.extend({
toBeWithinRange,
});
import {expect, test} from '@jest/globals';
import '../toBeWithinRange';
test('is within range', () => expect(100).toBeWithinRange(90, 110));
test('is NOT within range', () => expect(101).not.toBeWithinRange(0, 100));
test('asymmetric ranges', () => {
expect({apples: 6, bananas: 3}).toEqual({
apples: expect.toBeWithinRange(1, 10),
bananas: expect.not.toBeWithinRange(11, 20),
});
});
// optionally add a type declaration, e.g. it enables autocompletion in IDEs
declare module 'expect' {
interface AsymmetricMatchers {
toBeWithinRange(floor: number, ceiling: number): void;
}
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
}
}
export {};
import {expect} from '@jest/globals';
import type {MatcherFunction} from 'expect';
const toBeWithinRange: MatcherFunction<[floor: unknown, ceiling: unknown]> =
// `floor` and `ceiling` get types from the line above
// it is recommended to type them as `unknown` and to validate the values
function (actual, floor, ceiling) {
if (
typeof actual !== 'number' ||
typeof floor !== 'number' ||
typeof ceiling !== 'number'
) {
throw new TypeError('These must be of type number!');
}
const pass = actual >= floor && actual <= ceiling;
if (pass) {
return {
message: () =>
// `this` context will have correct typings
`expected ${this.utils.printReceived(
actual,
)} not to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${this.utils.printReceived(
actual,
)} to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: false,
};
}
};
expect.extend({
toBeWithinRange,
});
declare module 'expect' {
interface AsymmetricMatchers {
toBeWithinRange(floor: number, ceiling: number): void;
}
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
}
}
import {expect, test} from '@jest/globals';
import '../toBeWithinRange';
test('is within range', () => expect(100).toBeWithinRange(90, 110));
test('is NOT within range', () => expect(101).not.toBeWithinRange(0, 100));
test('asymmetric ranges', () => {
expect({apples: 6, bananas: 3}).toEqual({
apples: expect.toBeWithinRange(1, 10),
bananas: expect.not.toBeWithinRange(11, 20),
});
});
A declaração de tipo do matcher pode estar em um arquivo .d.ts ou em um módulo .ts importado (veja exemplos de JS e TS acima, respectivamente). Se você mantiver a declaração em um arquivo .d.ts, certifique-se de que ele está incluído no programa e que é um módulo válido, ou seja, que tenha pelo menos um export {} vazio.
Em vez de importar o módulo toBeWithinRange para o arquivo de teste, você pode habilitar o matcher para todos os testes movendo a chamada expect.extend para um script setupFilesAfterEnv:
import {expect} from '@jest/globals';
// remember to export `toBeWithinRange` as well
import {toBeWithinRange} from './toBeWithinRange';
expect.extend({
toBeWithinRange,
});
Matchers Assíncronos
expect.extend também suporta matchers assíncronos. Matchers assíncronos retornam uma Promise, então você precisará aguardar o valor retornado. Vamos usar um exemplo de matcher para ilustrar o uso deles. Implementaremos um matcher chamado toBeDivisibleByExternalValue, onde o número divisível será obtido de uma fonte externa.
expect.extend({
async toBeDivisibleByExternalValue(received) {
const externalValue = await getExternalValueFromRemoteSource();
const pass = received % externalValue === 0;
if (pass) {
return {
message: () =>
`expected ${received} not to be divisible by ${externalValue}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${received} to be divisible by ${externalValue}`,
pass: false,
};
}
},
});
test('is divisible by external value', async () => {
await expect(100).toBeDivisibleByExternalValue();
await expect(101).not.toBeDivisibleByExternalValue();
});
API de Matchers Personalizados
Matchers devem retornar um objeto (ou uma Promise de um objeto) com duas chaves. pass indica se houve correspondência ou não, e message fornece uma função sem argumentos que retorna uma mensagem de erro em caso de falha. Assim, quando pass é false, message deve retornar a mensagem de erro para quando expect(x).yourMatcher() falha. E quando pass é true, message deve retornar a mensagem de erro para quando expect(x).not.yourMatcher() falha.
Matchers são chamados com o argumento passado para expect(x) seguido pelos argumentos passados para .yourMatcher(y, z):
expect.extend({
yourMatcher(x, y, z) {
return {
pass: true,
message: () => '',
};
},
});
Estas funções auxiliares e propriedades podem ser encontradas em this dentro de um matcher personalizado:
this.isNot
Um booleano que informa se este matcher foi chamado com o modificador .not negado, permitindo que você exiba uma dica de matcher clara e correta (veja o código de exemplo).
this.promise
Uma string que permite exibir uma dica de matcher clara e correta:
-
'rejects'se o matcher foi chamado com o modificador.rejectsde promise -
'resolves'se o matcher foi chamado com o modificador.resolvesde promise -
''se o matcher não foi chamado com um modificador de promise
this.equals(a, b, customTesters?)
Esta é uma função de igualdade profunda que retornará true se dois objetos tiverem os mesmos valores (recursivamente). Opcionalmente, aceita uma lista de testadores de igualdade personalizados para aplicar nas verificações de igualdade profunda (veja this.customTesters abaixo).
this.expand
Um booleano que informa se este matcher foi chamado com a opção expand. Quando o Jest é chamado com a flag --expand, this.expand pode ser usado para determinar se o Jest deve exibir diffs e erros completos.
this.utils
Há várias ferramentas úteis expostas em this.utils, consistindo principalmente nas exportações de jest-matcher-utils.
As mais úteis são matcherHint, printExpected e printReceived para formatar as mensagens de erro de forma elegante. Por exemplo, veja a implementação do matcher toBe:
const {diff} = require('jest-diff');
expect.extend({
toBe(received, expected) {
const options = {
comment: 'Object.is equality',
isNot: this.isNot,
promise: this.promise,
};
const pass = Object.is(received, expected);
const message = pass
? () =>
// eslint-disable-next-line prefer-template
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
`Expected: not ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`
: () => {
const diffString = diff(expected, received, {
expand: this.expand,
});
return (
// eslint-disable-next-line prefer-template
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
(diffString && diffString.includes('- Expect')
? `Difference:\n\n${diffString}`
: `Expected: ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`)
);
};
return {actual: received, message, pass};
},
});
Isso imprimirá algo assim:
expect(received).toBe(expected)
Expected value to be (using Object.is):
"banana"
Received:
"apple"
Quando uma asserção falha, a mensagem de erro deve dar o máximo de informações necessárias ao usuário para que ele possa resolver seu problema rapidamente. Você deve elaborar uma mensagem de falha precisa para garantir que os usuários de suas asserções personalizadas tenham uma boa experiência de desenvolvimento.
this.customTesters
Se o seu matcher realiza uma verificação de igualdade profunda usando this.equals, você pode querer passar testadores personalizados fornecidos pelo usuário para this.equals. Os testadores de igualdade personalizados que o usuário forneceu usando a API addEqualityTesters estão disponíveis nesta propriedade. Os matchers internos do Jest passam this.customTesters (junto com outros testadores integrados) para this.equals para realizar a igualdade profunda, e seus matchers personalizados podem querer fazer o mesmo.
Matchers personalizados para snapshots
Para usar testes de snapshot dentro do seu matcher personalizado, você pode importar jest-snapshot e usá-lo diretamente no seu matcher.
Aqui está um exemplo de matcher de snapshot que armazena uma string truncada em um determinado comprimento, .toMatchTrimmedSnapshot(length):
const {toMatchSnapshot} = require('jest-snapshot');
expect.extend({
toMatchTrimmedSnapshot(received, length) {
return toMatchSnapshot.call(
this,
received.slice(0, length),
'toMatchTrimmedSnapshot',
);
},
});
it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10);
});
/*
Stored snapshot will look like:
exports[`stores only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`;
*/
Também é possível criar matchers personalizados para snapshots inline. Os snapshots serão adicionados corretamente aos matchers personalizados. Porém, snapshots inline sempre tentam anexar ao primeiro argumento ou ao segundo quando o primeiro é um matcher de propriedade, portanto não é possível aceitar argumentos personalizados nos matchers customizados.
const {toMatchInlineSnapshot} = require('jest-snapshot');
expect.extend({
toMatchTrimmedInlineSnapshot(received, ...rest) {
return toMatchInlineSnapshot.call(this, received.slice(0, 10), ...rest);
},
});
it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot(
`"extra long"`
);
*/
});
async
Se seu matcher de snapshot inline for assíncrono (ou seja, usar async-await), você pode encontrar erros como "Múltiplos snapshots inline para a mesma chamada não são suportados". O Jest precisa de informações contextuais adicionais para identificar onde o matcher de snapshot inline foi usado e atualizar os snapshots corretamente.
const {toMatchInlineSnapshot} = require('jest-snapshot');
expect.extend({
async toMatchObservationInlineSnapshot(fn, ...rest) {
// The error (and its stacktrace) must be created before any `await`
this.error = new Error();
// The implementation of `observe` doesn't matter.
// It only matters that the custom snapshot matcher is async.
const observation = await observe(async () => {
await fn();
});
return toMatchInlineSnapshot.call(this, recording, ...rest);
},
});
it('observes something', async () => {
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot(`"async action"`);
*/
});
Abortar execução
Normalmente o jest tenta corresponder a todos os snapshots esperados em um teste.
Às vezes pode não fazer sentido continuar o teste se um snapshot anterior falhou. Por exemplo, ao criar snapshots de uma máquina de estados após várias transições, você pode abortar o teste assim que uma transição produzir um estado incorreto.
Nesse caso, você pode implementar um matcher de snapshot personalizado que lança um erro no primeiro descompasso em vez de coletar todos os erros.
const {toMatchInlineSnapshot} = require('jest-snapshot');
expect.extend({
toMatchStateInlineSnapshot(...args) {
this.dontThrow = () => {};
return toMatchInlineSnapshot.call(this, ...args);
},
});
let state = 'initial';
function transition() {
// Typo in the implementation should cause the test to fail
if (state === 'INITIAL') {
state = 'pending';
} else if (state === 'pending') {
state = 'done';
}
}
it('transitions as expected', () => {
expect(state).toMatchStateInlineSnapshot(`"initial"`);
transition();
// Already produces a mismatch. No point in continuing the test.
expect(state).toMatchStateInlineSnapshot(`"loading"`);
transition();
expect(state).toMatchStateInlineSnapshot(`"done"`);
});
Propriedades serializáveis
SERIALIZABLE_PROPERTIES
Propriedades serializáveis são um conjunto de propriedades consideradas serializáveis pelo Jest. Este conjunto é usado para determinar se uma propriedade deve ser serializada ou não. Se um objeto tiver uma propriedade que não está neste conjunto, ela é considerada não serializável e não será exibida em mensagens de erro.
Você pode adicionar suas próprias propriedades a este conjunto para garantir que seus objetos sejam exibidos corretamente. Por exemplo, se você tem uma classe Volume e quer garantir que apenas as propriedades amount e unit sejam exibidas, você pode adicioná-las ao SERIALIZABLE_PROPERTIES:
import {SERIALIZABLE_PROPERTIES} from 'jest-matcher-utils';
class Volume {
constructor(amount, unit) {
this.amount = amount;
this.unit = unit;
}
get label() {
throw new Error('Not implemented');
}
}
Volume.prototype[SERIALIZABLE_PROPERTIES] = ['amount', 'unit'];
expect(new Volume(1, 'L')).toEqual(new Volume(10, 'L'));
Isso fará com que apenas as propriedades amount e unit sejam exibidas na mensagem de erro, ignorando a propriedade label.
expect(received).toEqual(expected) // deep equality
Expected: {"amount": 10, "unit": "L"}
Received: {"amount": 1, "unit": "L"}