Meu "Uau, eu não sabia disso!" momentos com brincadeira

Olá pessoal! O curso Desenvolvedor JavaScript inicia nesta quinta-feira. Nesse sentido, decidimos compartilhar a tradução de outro material interessante. Boa leitura.



Jest sempre foi minha ferramenta indispensável de teste de unidade. É tão confiável que começo a pensar que sempre o subutilizei. Embora os testes funcionassem, com o tempo eu os refatorei aqui e ali, porque não sabia que Jest poderia fazer isso. Cada vez que esse código é novo, verifico a documentação do Jest.

Então, vou compartilhar alguns dos meus truques favoritos do Jest que alguns de vocês já devem saber, porque leem a documentação e não gostam de mim (vergonha), mas espero que isso ajude aqueles que passaram por ela rapidamente !

A propósito, eu uso o Jest v24.8.0 como material de referência, portanto, tenha cuidado, algumas coisas podem não funcionar na versão do Jest que você está usando no momento. Além disso, os exemplos não representam o código de teste real, isso é apenas uma demonstração.

# 1 .toBe vs .toEqual


A princípio, todas essas declarações pareciam normais para mim:

expect('foo').toEqual('foo') expect(1).toEqual(1) expect(['foo']).toEqual(['foo']) 

Com base no uso de chai para declarações de igualdade (to.equal), isso é natural. De fato, Jest não vai reclamar, e essas declarações passam como de costume.

No entanto, o Jest possui .toBe e .toEqual. O primeiro é usado para afirmar a igualdade usando Object.is , e o segundo é fornecer uma comparação profunda de objetos e matrizes. .toEqual tem um fallback para usar Object.is, se você não precisar de uma comparação profunda, como afirmar igualdade de valores primitivos, o que explica por que o exemplo anterior foi muito bem.

 expect('foo').toBe('foo') expect(1).toBe(1) expect(['foo']).toEqual(['foo']) 

Dessa forma, você pode pular todo o if-else em .toEqual usando .toBe se você já sabe quais valores está testando.
Um erro comum é que você usará .toBe para afirmar a igualdade de valores primitivos.

 expect(['foo']).toBe(['foo']) 

Se você observar o código-fonte quando o .toBe travar, ele tentará determinar se você realmente cometeu esse erro chamando a função que o .toEqual usa. Isso pode ser um gargalo ao otimizar seu teste.

Se você tiver certeza de que está usando valores primitivos, seu código pode ser reorganizado para propósitos de otimização:

 expect(Object.is('foo', 'foo')).toBe(true) 

Mais detalhes na documentação .

# 2 Comparações mais adequadas


Tecnicamente, você pode usar .toBe para validar quaisquer valores. Com o Jest, você pode usar especificamente determinadas ferramentas de comparação para tornar seu teste mais legível (e em alguns casos mais curto).

 // expect([1,2,3].length).toBe(3) // expect([1,2,3]).toHaveLength(3) const canBeUndefined = foo() // expect(typeof canBeUndefined !== 'undefined').toBe(true) // expect(typeof canBeUndefined).not.toBe('undefined') // expect(canBeUndefined).not.toBe(undefined) // expect(canBeUndefined).toBeDefined() class Foo { constructor(param) { this.param = param } // expect(new Foo('bar') instanceof Foo).toBe(true) // expect(new Foo('bar')).toBeInstanceOf(Foo) 

Estes são apenas alguns dos que selecionei na longa lista de compiladores Jest na documentação. Você pode dar uma olhada no resto.

# 3 Teste de captura instantânea em elementos sem uma interface com o usuário


Você pode ter ouvido falar sobre o teste de captura instantânea no Jest , onde ajuda a rastrear alterações nos elementos da interface do usuário. Mas o teste com snapshots não se limita a isso.

Considere este exemplo:

 const allEmployees = getEmployees() const happyEmployees = giveIncrementByPosition(allEmployees) expect(happyEmployees[0].nextMonthPaycheck).toBe(1000) expect(happyEmployees[1].nextMonthPaycheck).toBe(5000) expect(happyEmployees[2].nextMonthPaycheck).toBe(4000) // ...etc 

Seria cansativo se você reivindicasse cada vez mais funcionários. Além disso, se houver necessidade de mais solicitações para cada funcionário, multiplique o número de novas solicitações pelo número de funcionários e você terá uma idéia.
Com o teste de instantâneo, tudo isso pode ser feito da seguinte maneira:

 const allEmployees = getEmployees() const happyEmployees = giveIncrementByPosition(allEmployees) expect(happyEmployees).toMatchSnapshot() 

Sempre que ocorrerem regressões, você saberá exatamente qual árvore no nó não corresponde à imagem.

Mas a comodidade tem um preço: esse método é mais suscetível a erros. Existe a possibilidade de você não saber que a foto está realmente incorreta e, no final, você a fotografará de qualquer maneira. Portanto, verifique seu snapshot como se fosse seu próprio código de aprovação (porque é).

Obviamente, o teste não se limita a instantâneos. Leia a documentação completa.

# 4 descrever.cada e testar.cada


Você já escreveu um teste que é um pouco semelhante a isso?

 describe('When I am a supervisor', () => { test('I should have a supervisor badge', () => { const employee = new Employee({ level: 'supervisor' }) expect(employee.badges).toContain('badge-supervisor') }) test('I should have a supervisor level', () => { const employee = new Employee({ level: 'supervisor' }) expect(employee.level).toBe('supervisor') }) }) describe('When I am a manager', () => { test('I should have a manager badge', () => { const employee = new Employee({ level: 'manager' }) expect(employee.badges).toContain('badge-manager') }) test('I should have a manager level', () => { const employee = new Employee({ level: 'manager' }) expect(employee.level).toBe('manager') }) }) 

Isso é monótono e rotineiro, certo? Imagine fazer isso com muitos casos.
Com description.each e test.each você pode compactar o código da seguinte maneira:

 const levels = [['manager'], ['supervisor']] const privileges = [['badges', 'toContain', 'badge-'], ['level', 'toBe', '']] describe.each(levels)('When I am a %s', (level) => { test.each(privileges)(`I should have a ${level} %s`, (kind, assert, prefix) => { const employee = new Employee({ level }) expect(employee[kind])[assert](`${prefix}${level}`) }) }) 

No entanto, ainda tenho que usar isso em meu próprio teste, pois prefiro que meu teste seja detalhado, mas achei que seria um truque interessante.

Consulte a documentação para obter mais detalhes sobre os argumentos (spoiler: a sintaxe da tabela é muito legal).

# 5 Imitação única de funções globais


Em algum momento, você terá que testar algo que depende das funções globais em um caso de teste específico. Por exemplo, uma função que recebe informações sobre a data atual usando o objeto javascript Date ou uma biblioteca que depende dele. A dificuldade é que, quando se trata da data atual, você nunca pode obter a declaração correta.

 function foo () { return Date.now() } expect(foo()).toBe(Date.now()) // This would throw occasionally: // expect(received).toBe(expected) // Object.is equality // // Expected: 1558881400838 // Received: 1558881400837 


No final, você teria que redefinir o objeto Date global para que ele seja consistente e gerenciável:

 function foo () { return Date.now() } Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // 

No entanto, isso é considerado uma prática ruim, pois a redefinição é mantida entre os testes. Você não notará isso se não houver outro teste baseado no Date.now, mas ele também vazará.

 test('First test', () => { function foo () { return Date.now() Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // ??? }) 

Eu costumava "quebrar" para não vazar:

 test('First test', () => { function foo () { return Date.now() const oriDateNow = Date.now Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // Date.now = oriDateNow }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // as expected }) 

No entanto, há uma maneira muito melhor e menos hacker de fazer isso:

 test('First test', () => { function foo () { return Date.now() jest.spyOn(Date, 'now').mockImplementationOnce(() => 1234567890123) expect(foo()).toBe(1234567890123) // }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // as expected }) 

Assim, o jest.spyOn segue o objeto global Data, imitando a implementação da função now para apenas uma chamada. Isso, por sua vez, deixará o Date.now intacto para o restante dos testes.

Definitivamente, há mais informações sobre stubs no Jest. Consulte a documentação completa para mais detalhes.

Este artigo está ficando longo o suficiente, então acho que é tudo por enquanto. Isso afeta apenas uma pequena parte dos recursos do Jest, e apenas destaquei meus favoritos. Se você tiver outros fatos interessantes, me avise.

Além disso, se você já usou o Jest com frequência, consulte o Majestic , que é uma interface gráfica do usuário sem interface gráfica para o Jest, uma alternativa muito boa à saída chata do terminal. Não sei se o autor está em dev.to, mas mesmo assim respeita essa pessoa.

Como sempre, obrigado pela atenção!

Só isso. Vejo você no curso.

Source: https://habr.com/ru/post/pt457616/


All Articles