No artigo anterior, examinamos aspectos teóricos. É hora de começar a praticar.

Vamos fazer uma implementação simples da pilha em JavaScript usando desenvolvimento por meio de testes.
Stack - uma estrutura de dados organizada de acordo com o princípio do LIFO: Last In, First Out. Existem três operações principais na pilha:
push : adicionar item
pop : excluir um item
peek : adicionar elemento principal
Crie uma classe e chame-a de pilha. Para complicar a tarefa, suponha que a pilha tenha uma capacidade fixa. Aqui estão as propriedades e funções de implementação da nossa pilha:
items : itens na pilha. Usaremos uma matriz para implementar a pilha.
capacidade :
capacidade da pilha.
isEmpty () : retorna true se a pilha estiver vazia, caso contrário, false.
isFull () : retorna true se a pilha atingir a capacidade máxima, ou seja, quando você não puder adicionar outro elemento. Caso contrário, retorna false.
push (elemento) : adiciona um elemento. Retorna Full se a pilha estiver cheia.
pop : remove o item. Retorna Vazio se a pilha estiver vazia.
peek () : adiciona um elemento principal.
Vamos criar dois arquivos
stack.js e
stack.spec.js . Usei a extensão
.spec.js porque estou acostumada, mas você pode usar .test.js ou dar um nome diferente e movê-lo para
__tests__ .
Como praticamos o desenvolvimento por meio de testes, escreveremos um teste com falha.
Primeiro, verifique o construtor. Para testar o arquivo, você precisa importar o arquivo de pilha:
const Stack = require('./stack')
Para aqueles que estão interessados em saber por que eu não usei a
importação aqui, a versão estável mais recente do Node.js não suporta esse recurso hoje. Eu poderia adicionar Babel, mas não quero sobrecarregá-lo.
Ao testar uma classe ou função, execute um teste e descreva qual arquivo ou classe você está testando. Isto é sobre a pilha:
describe('Stack', () => { })
Então, precisamos verificar se, quando a pilha é inicializada, uma matriz vazia é criada e configuramos a capacidade correta. Então, escrevemos o seguinte teste:
it('Should constructs the stack with a given capacity', () => { let stack = new Stack(3) expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) })
Observe que usamos
toEqual e não usamos
toBe para
stack.items porque eles não fazem referência à mesma matriz.
Agora execute o
teste de fios stack.spec.js .
Executamos o Jest em um arquivo específico porque não queremos que outros testes sejam corrompidos. Aqui está o resultado:
Pilha não é um construtor . Claro. Ainda não criamos nossa pilha e não criamos um construtor.
No
stack.js, crie sua classe, construtor e exporte a classe:
class Stack { constructor() { } } module.exports = Stack
Execute o teste novamente:

Como não definimos os elementos no construtor,
Jest esperava que os elementos na matriz fossem iguais a [], mas eles não foram definidos. Então você deve inicializar os elementos:
constructor() { this.items = [] }
Se você executar o teste novamente, receberá o mesmo erro de
capacidade , portanto, também será necessário definir a capacidade:
constructor(capacity) { this.items = [] this.capacity = capacity }
Execute o teste:

Sim
Teste aprovado . Aqui está o que é TDD. Espero que agora os testes façam mais sentido para você! Continuar?
isEmpty
Para testar
isEmpty , vamos inicializar uma pilha vazia, testar se isEmpty retorna true, adicionar um elemento e verificar novamente.
it('Should have an isEmpty function that returns true if the stack is empty and false otherwise', () => { let stack = new Stack(3) expect(stack.isEmpty()).toBe(true) stack.items.push(2) expect(stack.isEmpty()).toBe(false) })
Ao executar o teste, você deve receber o seguinte erro:
TypeError: stack.isEmpty is not a function
Para resolver esse problema, precisamos criar
isEmpty dentro da classe
Stack :
isEmpty () { }
Se você executar o teste, deverá receber outro erro:
Expected: true Received: undefined
Nada é adicionado ao
isEmpty .
A pilha está vazia se não houver elementos nela:
isEmpty () { return this.items.length === 0 }
isFull
É o mesmo que
isEmpty , pois este exercício testa essa função usando TDD. Você encontrará a solução no final do artigo.
Push
Aqui precisamos testar
três coisas:
- Um novo item deve ser adicionado ao topo da pilha.
- push retorna “Full” se a pilha estiver cheia;
- Um item que foi adicionado recentemente deve ser devolvido.
Crie outro bloco usando a
descrição para
envio . Colocamos esse bloco dentro do bloco principal.
describe('Stack.push', () => { })
Adicionar item
Crie uma nova pilha e adicione um elemento. O último item na matriz de
itens deve ser o item que você acabou de adicionar.
describe('Stack.push', () => { it('Should add a new element on top of the stack', () => { let stack = new Stack(3) stack.push(2) expect(stack.items[stack.items.length - 1]).toBe(2) }) })
Se você executar o teste, verá que o
push não
está definido e isso é normal.
push precisará de um parâmetro para adicionar algo à pilha:
push (element) { this.items.push(element) }
Teste passou novamente. Você notou alguma coisa? Mantemos uma cópia desta linha:
let stack = new Stack(3)
Isso é muito chato. Felizmente, temos um método
beforeEach que permite fazer algumas customizações antes de cada teste. Por que não tirar proveito disso?
let stack beforeEach(() => { stack = new Stack(3) })
Importante:
a pilha deve ser declarada antes de
beforeEach . De fato, se você o definir no método
beforeEach , a variável da pilha não será definida em todos os testes porque não está na área correta.
Mais importante do
que importante: também devemos criar um método
afterEach . A instância da pilha agora será usada para todos os testes. Isso pode causar algumas dificuldades. Imediatamente após
beforeEach, adicione este método:
afterEach(() => { stack.items = [] })
Agora você pode remover a inicialização da pilha em todos os testes. Aqui está o arquivo de teste completo:
const Stack = require('./stack') describe('Stack', () => { let stack beforeEach(() => { stack = new Stack(3) }) afterEach(() => { stack.items = [] }) it('Should constructs the stack with a given capacity', () => { expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) }) it('Should have an isEmpty function that returns true if the stack is empty and false otherwise', () => { stack.items.push(2) expect(stack.isEmpty()).toBe(false) }) describe('Stack.push', () => { it('Should add a new element on top of the stack', () => { stack.push(2) expect(stack.items[stack.items.length - 1]).toBe(2) }) }) })
Teste de valor de retorno
Existe um teste:
it('Should return the new element pushed at the top of the stack', () => { let elementPushed = stack.push(2) expect(elementPushed).toBe(2) })
Quando você executa o teste, você obtém:
Expected: 2 Received: undefined
Nada retorna dentro do
impulso ! Devemos corrigir isso:
push (element) { this.items.push(element) return element }
Retornar cheio se a pilha estiver cheia
Neste teste, primeiro precisamos preencher a pilha, adicionar um elemento, garantir que nada de supérfluo tenha sido adicionado à pilha e que o valor de retorno seja
Cheio .
it('Should return full if one tries to push at the top of the stack while it is full', () => { stack.items = [1, 2, 3] let element = stack.push(4) expect(stack.items[stack.items.length - 1]).toBe(3) expect(element).toBe('Full') })
Você verá este erro ao executar o teste:
Expected: 3 Received: 4
Então o item é adicionado. Isto é o que queríamos. Primeiro você precisa verificar se a pilha está cheia antes de adicionar algo:
push (element) { if (this.isFull()) { return 'Full' } this.items.push(element) return element }
Teste aprovado.Exercício: pop e peekÉ hora de praticar. Teste e implemente
pop and
peek .
Dicas:
- pop é muito semelhante ao push
- espiar também é como empurrar
- Até o momento, não refatoramos o código, porque isso não era necessário. Pode haver uma maneira nessas funções de refatorar seu código após escrever os testes e passá-los. Não se preocupe, alterando o código, testes são necessários para isso.
Não olhe para a solução abaixo sem tentar fazer isso sozinho. A única maneira de progredir é tentar, experimentar e praticar.
Solução
Bem, como está o exercício? Você fez isso? Caso contrário, não desanime. Testar leva tempo e esforço.
class Stack { constructor (capacity) { this.items = [] this.capacity = capacity } isEmpty () { return this.items.length === 0 } isFull () { return this.items.length === this.capacity } push (element) { if (this.isFull()) { return 'Full' } this.items.push(element) return element } pop () { return this.isEmpty() ? 'Empty' : this.items.pop() } peek () { return this.isEmpty() ? 'Empty' : this.items[this.items.length - 1] } } module.exports = Stack const Stack = require('./stack') describe('Stack', () => { let stack beforeEach(() => { stack = new Stack(3) }) afterEach(() => { stack.items = [] }) it('Should construct the stack with a given capacity', () => { expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) }) it('Should have an isEmpty function that returns true if the stack is empty and false otherwise', () => { expect(stack.isEmpty()).toBe(true) stack.items.push(2) expect(stack.isEmpty()).toBe(false) }) it('Should have an isFull function that returns true if the stack is full and false otherwise', () => { expect(stack.isFull()).toBe(false) stack.items = [4, 5, 6] expect(stack.isFull()).toBe(true) }) describe('Push', () => { it('Should add a new element on top of the stack', () => { stack.push(2) expect(stack.items[stack.items.length - 1]).toBe(2) }) it('Should return the new element pushed at the top of the stack', () => { let elementPushed = stack.push(2) expect(elementPushed).toBe(2) }) it('Should return full if one tries to push at the top of the stack while it is full', () => { stack.items = [1, 2, 3] let element = stack.push(4) expect(stack.items[stack.items.length - 1]).toBe(3) expect(element).toBe('Full') }) }) describe('Pop', () => { it('Should removes the last element at the top of a stack', () => { stack.items = [1, 2, 3] stack.pop() expect(stack.items).toEqual([1, 2]) }) it('Should returns the element that have been just removed', () => { stack.items = [1, 2, 3] let element = stack.pop() expect(element).toBe(3) }) it('Should return Empty if one tries to pop an empty stack', () => {
Se você olhar para os arquivos, verá que eu usei a condição tripla no pop e peek. Foi isso que refatorei. A implementação antiga era assim:
if (this.isEmpty()) { return 'Empty' } return this.items.pop()
O desenvolvimento por meio de testes nos permite refatorar o código após a gravação dos testes. Encontrei uma implementação mais curta sem me preocupar com o comportamento dos meus testes.
Execute o teste novamente:

Os testes melhoram a qualidade do código. Espero que agora você entenda todos os benefícios dos testes e use o TDD com mais frequência.
Nós convencemos você de que o teste é crucial e melhora a qualidade do código? Em vez disso, leia a parte 2 do artigo sobre desenvolvimento através de testes. E quem não leu o primeiro,
siga o link .