Skip to content

Instantly share code, notes, and snippets.

@lai32290
Last active November 5, 2017 19:35
Show Gist options
  • Save lai32290/4b92e5d38602c3ca0f187eb4c90911e0 to your computer and use it in GitHub Desktop.
Save lai32290/4b92e5d38602c3ca0f187eb4c90911e0 to your computer and use it in GitHub Desktop.
Javascript Unit Test

Público Alvo

Programadores familiarizado com Javascript, porém sem/pouca experiencia em unit test.

O que Será Abordado

Esse post irá abordar vários casos de testes e como eles podem ser testados, de uma forma bem direta e objetiva.

Testes

Como hoje existe vários bibliotecas para realizar unit test, cada um tem suas características, os exemplos usado neste artigo será usado Jasmine, porém todos eles podem ser realizados em outras bibliotecas de unit test também.

Retorno da função está correto?

Para verificar se o resultado do retorno da função é igual a tal valor, existe a função toEqual, essas funções que vem acompanhados dos expect, são chamados de matchers, existe muits outras além do toEqual na documentação.

const sum = (value1, value2) => value1 + value2;

describe("Testando retorno da funcao", () => {
  it("deve retornar a soma dos valores 1 e 2", () => {
    const result = sum(1, 2); // pegando resultado da soma de 1 e 2
    
    expect(result).toEqual(3); // verificar se o resultado retornado é igual a 3
  });
});

O JSON contem essa propriedade?

O Jasmine oferece a função objectContaining, que serve para verificar se tal JSON possui certa propriedade.

function concatObject(obj1, obj2) {
  return { ...obj1, ...obj2 };
}

describe("JSON contains propriedade", () => {
  it("deve conter CPF no retorno", () => {
    const pessoa = { nome: "Paulo", idade: 18 };
    const result = concatObject( pessoa, { cpf: "123.456.789-10" } );

    expect( result ).toEqual( jasmine.objectContaining( { cpf: "123.456.789-10" } ));
  });
});

A função está sendo chamado?

Verificar se a função esta executando outra funções corretamente.

import $ from "jquery";

function fetchUsers() {
  $.get("/my/api/to/get/users", (res) => {
    // faz alguma coisa
  });
}

describe("Testando se a funcao foi chamado", () => {
  it("deve chamar funcao get do jquery", () => {
    spyOn($, "get"); // tornar a funcao $.get possivel de ser analisado
    fetchUsers();
    
    expect($.get).toHaveBeenCalled(); // verificar se o $.get foi chamado, após a execução da função getUsers
  });
});

A função spyOn irá tornar o função $.get possivel de ser monitorado, após a chamada da função fetchUsers, é possivel verificar com toHaveBeenCalled, se a função realmente foi executado.

Quantas vezes a função foi executado?

Para verificar quantas vezes a função foi executado, o Jasmine oferece a função .calls.count(), que retorna a quantidade de vezes que a função foi executado.

describe("Testando funcao", () => {
  it("A funcao deve ser executado 3 vezes", () => {
    const somaFake = jasmine.createSpy("contaFake");

    [1, 2, 3].reduce(somaFake, 0);
    expect(somaFake.calls.count()).toEqual(3);
  });
});

jasmine.createSpy irá criar uma função monitoravel com algumas funções que ajuda o teste, entre elas o .calls.count(). Verifique outras funções na documentação do Jasmine

A função está recebendo parâmetros corretos?

Para ser mais específico, também é possivel verificar quais parametros foram passados para a função chamado.

import $ from "jquery";

const callback = (res) => {
  // faz alguma coisa
};

function getUsers() {
  $.get("/my/api/to/get/users", callback);
}

describe("Testando se a funcao foi chamado", () => {
  it("deve chamar funcao get do jquery", () => {
    spyOn($, "get"); // tornar a funcao $.get possivel de ser analisado
    getUsers();
    
    expect($.get).toHaveBeenCalledWith("/my/api/to/get/users", callback);
  });
});

A função toHaveBeenCalledWith verifica se os parâmetros recebidos são os mesmos recebido pela função $.get.

Testando componente com ReactTestUtils

O pacote react-dom possui as funções para renderização e testes de componentes.

import TestUtils from 'react-dom/test-utils';

const MeuComponente = (props) => {
  return <div>
    <input className="campo-nome" type="text" value={props.nome} />
    Hello {props.nome}
  </div>
};

describe("Testando componente com ReactTestUtils", () => {
  it("o campo de nome deve conter o nome recebido na propriedade", () => {
    const element = TestUtils.renderIntoDocument(<MeuComponente nome="John" />);
    
    expect(TestUtils.findRenderedDOMComponentWithClass(elemento, "campo-nome").value).toEqual("John");
  });
});

Após da renderização com a função renderIntoDocument, é possivel obter o elemento com class campo-nome utilizando a função findRenderedDOMComponentWithClass e testando o valor do campo de texto.

O componente está renderizando corretamente?

Geralmente os componentes do React se comportam conforme as propriedades recebida, nesse caso pode ser usado o mock da propriedade para testar os diversos comportamentos do componente. Existe a biblioteca enzyme, que é recomendado no próprio site oficial do React, usado para renderizar o componente React com objetivo de teste.

import { shallow } from "enzyme";

const MeuComponente = (props) => {
  return <div>
    Hello {props.nome}
  </div>
};

describe("Testando componente com propriedades mock", () => {
  it("deve conter Hello John", () => {
    const wrapper = shallow(<MeuComponente nome="John" />);
    
    expect(wrapper.text()).toEqual("Hello John");
  });
});

Como criar nova instancia de componente, automaticamente, antes de cada teste?

Quando precisa testar as funções dos componentes, é muito comum criar uma instancia nova do componente para cada teste, garantindo que o estado inicial dos testes, para evitar o trabalho repetitivo de instanciar componente, existe a função beforeEech:

import React, {Component } from "react";
import { shallow } from "enzyme";

class MeuComponente extends Component {
  construct(props) {
    super(props);
    this.state = {
      nome: props.nome
    };
    this.sayHi = this.sayHi.bind(this);
  }

  mudarNome(nome) {
    this.setState({ nome });
  }

  render() {
    return <div>
      Hello {this.state.nome}
    </div>
  }
}

describe("Criando instancia nova do componente para cada teste de componente", () => {
  let wrapper;

  beforeEach(() => {
    wrapper = shallow(<MeuComponente nome="John" />);
  });

  it("deve conter Hello Paul", () => {
    wrapper.instance().mudarNome("Paul");
    expect(wrapper.text()).toEqual("Hello Paul");
  });

  it("deve conter Hello John", () => {
    expect(wrapper.text()).not.toEqual("Hello John");
  });
});

Para esse teste, foi feito a alteração no componente MeuComponente para exibir o state.nome, envez de props.nome, e pode notar que no primeiro teste, o nome foi alterado de John para Paul, mas o segundo teste permanece sucedido, isso é por que o componente foi instanciado novamente antes de executar o código de teste.

Além de beforeEach, também existe afterEach, beforeAll e afterAll.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment