Jak już kiedyś wspominałem w poście dotyczącym testowania(znajdziecie go tutaj) testowanie jest ważnym elementem podczas tworzenia oprogramowania. Dziś chciałbym poświęcić chwilę czasu na testy jednostkowe w React’cie. Testy jednostkowe powinniśmy tworzyć dla właściwie każdego komponentu dzięki czemu będziemy mieć pewność, że działa poprawnie i niczego nie psujemy podczas zmian lub ulepszania kodu.

Jest i Enzyme

Oficialne narzędzie create-react-app, które posłużyło mi do stworzenia szkieletu aplikacji korzysta z frameworka Jest do uruchamiania testów. Oprócz tego zdecydowałem się skorzystać z biblioteki Enzyme, która ułatwia testowanie komponentu o czym będzie więcej za chwilę. Najpierw parę słów poświęcę Jest’owi. Aby uruchomić testy musimy się trzymać pewnej konwencji, którą narzuca nam Jest a mianowicie sposób tworzenia plików, które ma uruchomić. Mamy tutaj 3 opcje:

Pliki te mogą w dowolnej lokalizacji wewnątrz folderu src. Zalecane jest również by pliki te były jak najbliżej testowanych komponentów. Ja osobiście preferuję pliki o nazwie *.spec.js, które umieszczam w tym samym folderze co komponent.

Aby napisać test musimy w nim umieścić blok it(), który zawiera nazwę testu oraz funkcję, która go przeprowadzą. Można jeszcze umieścić bloki it() wewnątrz bloku describe() aby bardziej poukładać kod. Nie jest to wymagane jednak ja z tego korzystam, ponieważ pomaga w poukładaniu testów. Taki przykładowy szblon pliku testowego wygląda następująco:

describe('NazwaKomponentu',()=>{
    it('renders compononent',()=>{
        //ciało funkcji które przeprowadzi test
    })
})

Warto również tutaj wspomnieć, że plik testowy musi zawierać przynajmniej jeden test(blok it()), ponieważ w innym przypadku będzie wyrzucał następujący błąd:

empty_test

Każde ciało funkcji, które będzie przeprowadzało test będzie zawierać następującą konstrukcję

expect(…).toEqual(…)

Jest to dosyć proste i wykorzystuje naturalny język. Wewnątrz funkcji expect musimy umieścić to co chcemy testować np.: funkcję, natomiast wewnątrz funkcji sprawdzajacej toEqual() spodziewany przez nas wynik np.:

Expect(silnia(0)).toEqual(1)

Zamiast funkcji toEqual możemy stosować inne dostarczone nam przez Jest funkcje np.: toBe(), toHaveBeenCalled(), toContain() itd. Całą listę możecie zobaczyć tutaj

Enzyme

W takim razie do czego nam jest potrzebny Enzyme? Ano do renderowania komponentu i wyszukiwania w nim istotnych dla nas elementów. Chodzi o to by podczas testowania komponentu móc w nim znaleźć szukany przez nas element np. p i zwrócić na przykład tekst, który w nim się znajduje. Zanim opiszę pokrótce tą bibliotekę to musimy ją zainstalować przy pomocy następujących komend:

yarn  add enyme 
yarn add react-test-renderer react-dom

W Enzymie mamy 3 sposoby tworzenia komponentu:

Tworzenie komponentu wewnątrz testów może wyglądać następująco:

import {shallow} from 'enzyme'
const wrapper = shallow(<Komponent />)

W tak stworzonym komponencie możemy wyszukiwać elementy np.: przy pomocy funkcji find lub też zwracać tekst przy pomocy funkcji text(). Możemy również zarządzać zmiennymi props i state z komponentu. Wszystkie dostępne metody możecie znaleźć tutaj

Przetestujmy cos wreszcie

Wstęp troszkę mi zajął ale był potrzebny by teraz przejść do właściwiej części postu. Chciałbym teraz pokazać jak można przetestować prosty komponent, który wyświetla liczbę kliknięć przycisku. Kod komponentu wygląda następująco:

export class CounterComponent extends Component{
    state = {
        counter:0
    }

    increaseCounter = ()=>{
        this.setState(prev=>({
            counter: prev.counter+1
        }))
    }
    render(){
        return(
            <div>
                <p>Counter:</p>
                <span>{this.state.counter}</span>

                <button onClick={this.increaseCounter}>Click me</button>
            </div>
        )
    }
}

Nie jest on duży ale został specjalnie dobrany by pokazać parę ciekawych rzeczy. Testowanie komponentów zwykle zaczynam od sprawdzenia czy wyświetla wszystkie potrzebne mi elementy. Dla tego komponentu może to wyglądać następująco:

it('renders compononent',()=>{
    const wrapper = shallow(<CounterComponent/>)
    expect(wrapper.find('p').text()).toEqual('Counter:')
    expect(wrapper.find('span').text()).toEqual('0')
})

Widać tutaj przykład połączenia Jest’a i Enzym’a. Za pomocą tego drugiego wyszukujemy interesujący nas element by potem zwrócić tekst do porównania. Oprócz samego sprawdzenia czy komponent jest tworzony ze wszystkimi elementami możemy sprawdzić czy właściwie reaguje na zmiany wartości props lub state.

it('changes value in span according to state',()=>{
    const wrapper = shallow(<CounterComponent/>)
    expect(wrapper.find('span').text()).toEqual('0')
    wrapper.setState({counter:10})
    expect(wrapper.find('span').text()).toEqual('10')
})

Tutaj wykorzystuję jedną z funkcji Enzyme by zmienić wartość counter w obiektcie store a następnie sprawdzam czy mój komponent właściwie to wyświetlił. No i jeszcze został nam przycisk który zwiększa wartość counter o 1. To również możemy przetestować:

it('increases state counter value by one on click', ()=>{
    const wrapper = shallow(<CounterComponent/>)
    expect(wrapper.find('span').text()).toEqual('0')
    wrapper.find('button').simulate('click')
    expect(wrapper.find('span').text()).toEqual('1')
})

Aby zasymulować przeciśnięcie przycisku przez użytkownika wykorzystałem funkcję pochodzącą z Enzyme o nazwie simulate(). Na sam koniec warto puścić testy w konsoli przy pomocy polecenia yarn test i zobaczyć czy wszystkie przechodzą na zielono.

green_test

Jeśli w trakcie rozwijania kodu jakiś zmieni się na czerwono to znaczy, że albo coś popsuliśmy i musimy poprawić kod lub zmiana kodu była zrobiona specjalnie i musimy poprawić testy

A wy czego używacie do testowania i czy w ogóle testujecie komponenty? Napiszcie też czy wam się podobało i czy chcecie więcej postów dotyczących testowania różnych elementów. No i jak macie jakieś problemy to śmiało piszcie a może uda mi się pomóc a jeśli zbierze się więcej takich ciekawych rzeczy to stworzę z tego osobny post :)