Testar se uma função trata corretamente erros é algo comum em testes unitários. Quando esse tratamento ocorre por meio de exceções, é fácil se perder frente à quantidade de possibilidades - além do fato de não ser tão trivial quanto parece.
Antes de mais nada, neste tutorial eu assumo que:
- Você está familiarizado com
async/await
; - Você já utilizou o jest e sabe o contexto de termos como
describe
eit
; - Você sabe lidar com exceções.
O problema
Imagine as seguintes funções:
// funcs.jsconstfoo=()=>{thrownewError('Something wrong');}constgoo=async()=>{thrownewError('Something wrong - async');}constboo=(n)=>{if(n<0){thrownewError('Something wrong - params');}}constdoo=async(n)=>{if(n<0){thrownewError('Something wrong - async + params');}}exportdefault{foo,goo,boo,doo};
Como seria possível testar se a exceção é lançada corretamente? Você pode pensar em algo assim para a funçãofoo
, por exemplo:
// funcs.test.jsimport{foo}from'./funcs.js'test('Should throw exception',()=>{try{foo();}catch(e){expect(e.message).toBe('Something wrong');}});
Contudo esse teste não seria muito eficiente, uma vez que ele poderia passar quando nenhuma exceção está sendo lançada. Além disso, o Jest nos possibilita fazer algo muito mais simples, sem a necessidade de escrever uma estrutura tão grande.
Funções Síncronas sem parâmetros (foo)
Esse é o caso mais simples. Basta passar a função como parâmetro para oexpect
e utilizar o métodotoThrow
(ou similares). Nesse caso, teríamos algo assim:
import{foo}from'./funcs.js'test('Should throw exception',()=>{expect(foo).toThrow();});
Caso queira verificar o tipo de erro, você pode utilizar o
toThrowError
:expect(foo).toThrowError(newError('Something wrong'));
Funções Síncronas com parâmetros (boo)
Aqui temos uma pequena diferença. Vamos chamar a função com um dado que deve gerar exceções dentro de uma função anônima e passar esta para oexpect
:
import{boo}from'./funcs.js'test('When n < 0, expect to throw Error',()=>{expect(()=>boo(-1)).toThrow();});
Funções assíncronas (goo, doo)
Aqui teremos duas mudanças. A primeira é que a função passada para otest
/it
deve ser assíncrona. A outra é que vamos chamar a função diretamente dentro doexpect
e testar se a Promise será rejeitada e retornará um erro:
import{goo,doo}from'./funcs.js'test('Expect to throw Error',async()=>{awaitexpect(goo()).rejects.toThrow();});test('When n < 0, expect to throw Error',async()=>{awaitexpect(doo(-1)).rejects.toThrow();});
Nas funções assíncronas podemos realizar chamadas diretamente pelo fato delas não lançarem a exceção diretamente, mas retornarem Promises rejeitadas. Caso o mesmo seja feito para funções síncronas, o teste será interrompido e não funcionará corretamente.
Top comments(4)

Júnior, estou iniciando nos testes e talvez você possa me ajudar... Tenho uma função de login com try/catch que recebe um email e um password, se o e-mail e senha não batem ela exibe um alert na tela. Montei o teste para essa função, passando os parâmetros que o backend aceita, mas o teste quebra no catch. Vou colá-los aqui:
Função login:
"async function Login(loginData) {
try {
const response = await api.post('/user/login', loginData);
setUser(response.data);
localStorage.setItem('@App:user', JSON.stringify(response.data));
localStorage.setItem('@App:token', response.data.id);
api.defaults.headers.Authorization =Bearer ${response.data.id}
;
} catch(error) {
alert(Usuário ou senha inválidos!
);
}
}"
Teste:
"describe('Login function', () => {
it('Set the user to logged', () => {
const TestComponent = () => {
const { Login, logged } = useAuth();
const loginData = {email:'teste@front.com', password:'teste123'};
return (
<>
</>
);
}
render();
expect(screen.getByTestId('status')).toHaveTextContent('false')
fireEvent.click(screen.getByTestId('button'))
expect(screen.getByTestId('status')).toHaveTextContent('true')
})
});"
Sabe me dizer o que está errado? Agradeço demais o help, é muito difícil achar conteúdo sobre testes, principalmente para funções com providers..rsrs

- LocationRecife, Brazil
- EducationElectronic Engineering (incomplete) @ UFPE
- WorkJavascript / Typescript Developer
- Joined
Oi Fabi, tudo bem? Percebi que nesse trecho que tu me mandou, não tem nenhum mock pra API do Axios. Isso foi apenas por simplicidade ou de fato está batendo no backend durante o teste? Talvez sso poderia fazer com que a chamada pra API sempre retorne um erro.
De toda forma, o intuito seria testar apenas a lógica da função Login? Penso que poderia ser mais simples, se for o caso, testar ela diretamente, sem usar esse TestComponent.

Então, fiz um teste com mock pra testar se a função Login está sendo chamada ao clicar no botão, esta parte está ok.
Mas eu queria testar neste caso o estado do usuário, se após os dados serem validados o status dele passa para logged=true. Fiz a função enviado os dados para bater no backend(api) para testar essa validação... Tenho pouca experiência com testes, talvez eu esteja testando algo indevido também, rsrsrs
Este era um passo de um teste que me enviaram onde pediam testes de snapshot com jest...não consegui visualizar como o snapshot poderia se encaixar neste contexto, então tentei fazer assim...rs

- Email
- LocationRecife/Brazil
- EducationComputer engineering undergraduate at the Federal University of Pernambuco | Brazil
- Pronounshe/him
- WorkSoftware Engineer
- Joined
Ótimo conteúdo Júnior! Ansioso para mais!
For further actions, you may consider blocking this person and/orreporting abuse