Narzędzia branżowe

Zautomatyzowane testy to zasadniczo kod, który generuje lub powoduje błąd, jeśli coś poszło nie tak. Większość bibliotek i platform testowych zapewnia różne elementów podstawowych, które ułatwiają pisanie testów.

Jak wspomnieliśmy w poprzedniej sekcji, te elementy podstawowe niemal zawsze zawierają wyznaczania niezależnych testów (nazywanych przypadkami testowymi) i dostarczania asercji. Asercje to sposób połączenia sprawdzania wyniku z przesyłaniem żądania gdy coś jest nie tak i można go uznać za podstawowy czyli testowania elementów podstawowych.

Ta strona zawiera ogólne podejście do tych elementów podstawowych. Wybrana przez Ciebie platforma prawdopodobnie ma coś takiego, ale nie jest to dokładne odniesienie.

Na przykład:

import { fibonacci, catalan } from '../src/math.js';
import { assert, test, suite } from 'a-made-up-testing-library';

suite('math tests', () => {
  test('fibonacci function', () => {
    // check expected fibonacci numbers against our known actual values
    // with an explanation if the values don't match
    assert.equal(fibonacci(0), 0, 'Invalid 0th fibonacci result');
    assert.equal(fibonacci(13), 233, 'Invalid 13th fibonacci result');
  });
  test('relationship between sequences', () => {
    // catalan numbers are greater than fibonacci numbers (but not equal)
    assert.isAbove(catalan(4), fibonacci(4));
  });
  test('bugfix: check bug #4141', () => {
    assert.isFinite(fibonacci(0)); // fibonacci(0) was returning NaN
  })
});

W tym przykładzie tworzona jest grupa testów (czasami nazywana pakietem) o nazwie „matematyka”. (testy) i zdefiniowano 3 niezależne przypadki testowe, w których każdy przeprowadza pewne asercje. Takie przypadki testowe można zwykle rozwiązać lub przeprowadzić, na przykład przez flagi filtra w narzędziu testowym.

Asystent asercji jako elementy podstawowe

Większość platform testowych, w tym Vitest, zawiera zbiór asercji w obiekcie assert, które pozwalają szybko sprawdzić zwracane wartości, z niektórymi oczekiwaniami. To oczekiwanie często jest określane jako „znane dobra”. . W poprzednim przykładzie wiemy, że trzynasta liczba Fibonacciego powinna wynosić 233, więc możemy potwierdzić, że to bezpośrednio za pomocą assert.equal.

Możesz też mieć oczekiwania, że wartość ma określoną postać większą niż inna wartość lub mają inną właściwość. Ten kurs nie: obejmują pełną gamę pomocników asercji, zawsze przeprowadzaj przynajmniej te podstawowe kontrole:

  • „Prawda” kontrola, często opisana jako „OK” sprawdza, czy warunek jest spełniony tak jak można napisać if, który sprawdza, czy coś się udało Zgadza się. Zwykle podaje się ją jako assert(...) lub assert.ok(...), przyjmuje pojedynczą wartość plus opcjonalny komentarz.

  • Sprawdź równość, np. w przykładzie matematycznym, w którym oczekujesz zwracaną wartość lub stan obiektu, aby równać się znanej dobrej wartości. Są one przeznaczone dla równość podstawowa (np. w przypadku liczb i ciągów znaków) lub równość referencyjna (to ten sam obiekt). W głowie się nie mieści, to jest po prostu „prawda” zaznacz z porównaniem == lub ===.

    • W języku JavaScript różni się loczna (==) i ścisła (===) równość. Większość bibliotek testowych udostępnia metody assert.equal i assert.strictEqual.
  • Szczegółowa kontrola równości, która obejmuje sprawdzanie równości zawartości obiektów, tablic i innych bardziej złożonych typów danych, a także dzięki wewnętrznej logice, aby przemierzać obiekty i je porównywać. To ważne: bo JavaScript nie ma wbudowanego sposobu na porównywanie zawartości dwóch obiektów lub tablic. Na przykład [1,2,3] == [1,2,3] ma zawsze wartość fałsz. Testuj platformy często zawierają pomocniki deepEqual lub deepStrictEqual.

Asystent porównawczy asercji, który porównuje 2 wartości (a nie tylko test „prawdziwość”) zwykle przyjmuje się 2 lub 3 argumenty:

  • Rzeczywista wartość wygenerowana na podstawie testowanego kodu lub opisująca do weryfikacji.
  • Wartość oczekiwana, zwykle zakodowana na stałe (np. liczba dosłowna lub ciąg znaków).
  • Opcjonalny komentarz opisujący, co jest spodziewane lub co mogło zostać nieskuteczne. który zostanie uwzględniony w przypadku niepowodzenia tej linii.
.

Powszechną praktyką jest też łączenie asercji w celu utworzenia różnych sprawdza się, ponieważ rzadko można prawidłowo potwierdzić stan przez system. Na przykład:

  test('JWT parse', () => {
    const json = decodeJwt('eyJieSI6InNhbXRob3Ii');

    assert.ok(json.payload.admin, 'user should be admin');
    assert.deepEqual(json.payload.groups, ['role:Admin', 'role:Submitter']);
    assert.equal(json.header.alg, 'RS265')
    assert.isAbove(json.payload.exp, +new Date(), 'expiry must be in future')
  });

Vitest używa biblioteki asercji Chai wewnętrznie, aby zapewnić swoim pomocnikom asercji, i przydatne może być analizowanie odniesienie do tego, które asercje i pomocniki mogą pasować do Twojego kodu.

Asercje o biegłości i BDD

Niektórzy deweloperzy preferują styl asercji, który można nazwać opartym na zachowaniach. (BDD) lub Styl Fluent asercji. Są również nazywane „oczekiwaniami”. ponieważ punkt wejścia sprawdzanie oczekiwań to metoda o nazwie expect().

Spodziewaj się, że elementy pomocnicze będą zachowywać się tak samo jak asercje zapisane jako metoda prosta takie jak assert.ok lub assert.strictDeepEquals, ale niektórzy deweloperzy uznają je za czytelniejsze. Potwierdzenie BDD może wyglądać tak:

// A failure here would generate "Expect result to be an array that does include 42"
const result = await possibleMeaningsOfLife();
expect(result).to.be.an('array').that.does.include(42);

// or a simpler form
expect(result).toBe('array').toContainEqual(42);

// the same in assert might be
assert.typeOf(result, 'array', 'Expected the result to be an array');
assert.include(result, 42, 'Expected the result to include 42');

Ten styl asercji działa dzięki metodzie zwanej łańcuchem metod, w którym obiekt zwrócony przez funkcję expect może być stale połączony w łańcuch, kolejnych wywołań metod. Niektóre części rozmowy, w tym to.be i that.does w poprzednim przykładzie nie mają żadnej funkcji i są uwzględniane tylko w celu wywołania być bardziej czytelne i potencjalnie generować automatyczny komentarz, jeśli test niepowodzenie. (W szczególności funkcja expect zwykle nie obsługuje opcjonalnego komentarza, ponieważ łańcuch powinien jasno opisywać błąd).

Wiele platform testowych obsługuje zarówno twierdzenia Fluent/BDD, jak i zwykłe asercje. Vitest, na przykład eksportuje zarówno Chai ma nieco bardziej zwięzłe podejście do BDD. Jest z drugiej strony zawiera tylko oczekiwany wynik .

Grupuj testy w plikach

Podczas pisania testów zwykle umieszczamy niejawne grupowanie. niż wszystkie testy w jednym pliku, często zapisuje się je w wielu różnych . Uczestnicy testu zwykle wiedzą, że plik jest przeznaczony do testów, ponieważ wstępnie zdefiniowanego filtra lub wyrażenia regularnego – na przykład vitest obejmuje wszystkie pliki w projekcie, które mają rozszerzenie „.test.jsx”; lub „.spec.ts” („.test” i „.spec” oraz kilka prawidłowych rozszerzeń).

Testy komponentów znajdują się zwykle w pliku równorzędnym z testowanym komponentem. jak w następującej strukturze katalogów:

Lista plików w
  w tym UserList.tsx i UserList.test.tsx.
Plik komponentu i powiązany plik testowy.

Testy jednostkowe również są umieszczane obok testowanego kodu. Kompleksowe testy mogą znajdować się w osobnych plikach, a testy integracji mogą nawet i umieszczać je w osobnych folderach. Takie struktury mogą być pomocne, złożone przypadki testowe coraz częściej wymagają własnych plików pomocniczych, takich jak obsługują biblioteki potrzebne tylko do testów.

Grupowe testy w plikach

Jak wspomnieliśmy w poprzednich przykładach, często przeprowadza się testy w obrębie wywołania funkcji suite(), który grupuje testy skonfigurowane za pomocą narzędzia test(). Mieszkania nie są zwykle samodzielnie testują, ale pomagają stworzyć strukturę, grupując powiązane testy. lub celów, wywołując przekazaną metodę. W przypadku test() przekazana metoda opisuje na samym początku testu.

Podobnie jak w przypadku asercji, w Fluent/BDD istnieje dość standardowy odpowiednik grupowania testów. Poniżej znajdziesz porównanie kilku typowych przykładów:

// traditional/TDD
suite('math tests', () => {
  test('handle zero values', () => {
    assert.equal(fibonacci(0), 0);
  });
});

// Fluent/BDD
describe('math tests', () => {
  it('should handle zero values', () => {
    expect(fibonacci(0)).toBe(0);
  });
})

W większości platform suite i describe działają podobnie, podobnie jak test i it, w odróżnieniu od większych różnic między używaniem wartości expect i assert do pisania asercji.

Inne narzędzia stosują nieco inne podejście do porządkowania pakietów i testów. Dla: na przykład wbudowany mechanizm uruchamiania testów w Node.js obsługuje zagnieżdżanie wywołań funkcji test() w celu niejawnie utworzyć hierarchię testową. Vitest zezwala jednak tylko na ten rodzaj zagnieżdżenie za pomocą suite() i nie uruchomi żadnego elementu test() zdefiniowanego w innym obiekcie test().

Podobnie jak w przypadku asercji, pamiętaj, że dokładne połączenie grupowania stosowane przez Twój stos technologiczny nie są aż tak ważne. Tematy poruszane w tym kursie to abstrakcyjne, ale musisz przemyśleć, jak będą się one stosować szeroki wybór narzędzi.

Metody cyklu życia

Jednym z powodów, dla których warto grupować testy, nawet pośrednio na najwyższym poziomie w obrębie jest udostępnienie metod konfiguracji i dezaktywacji, które działają przy każdym teście, dla grupy testów. Większość platform udostępnia 4 metody:

Dla każdej funkcji „test()” lub „it()” Raz na pakiet
Przed uruchomieniami testów „beforeEach()” `beforeAll()`
Po przeprowadzeniu testów „afterEach()” „afterAll()”

Możesz na przykład wstępnie wypełnić wirtualną bazę danych użytkowników przed każdym a potem usuń go:

suite('user test', () => {
  beforeEach(() => {
    insertFakeUser('bob@example.com', 'hunter2');
  });
  afterEach(() => {
    clearAllUsers();
  });

  test('bob can login', async () => {  });
  test('alice can message bob', async () => {  });
});

Pozwala to uprościć testy. Możesz udostępnić wspólną konfigurację i w kodzie dezaktywacji, zamiast powielać go w każdym teście. Dodatkowo, jeśli Sam kod konfiguracji i dezaktywacji powoduje błąd, który może oznaczać, które nie są związane z niepowodzeniem samych testów.

Porady ogólne

Oto kilka wskazówek, o których warto pamiętać, analizując te elementy podstawowe.

Przewodniki po elementach podstawowych

Pamiętaj, że narzędzia i elementy podstawowe dostępne tutaj i na kilku kolejnych stronach nie będą ściśle pasuje do Vitest, Jest, Mocha, Web Testner lub innego określonych ram. Vitest jest naszym ogólnym przewodnikiem, ale pamiętaj, zgodnie z wybranym schematem.

Mieszaj i dopasowuj asercje w razie potrzeby

Testy to z reguły kod, który może powodować błędy. Każdy biegacz zapewni zwykły (prawdopodobnie test()), aby opisać różne przypadki testowe.

Jeśli jednak ten biegacz udostępnia również pomocniki assert(), expect() i asercji, pamiętaj, że ta część dotyczy wygody. Można ją pominąć, jeśli co trzeba. Możesz uruchomić dowolny kod, który może generować błąd, w tym inny kod biblioteki asercji lub starą dobrą instrukcję if.

Konfiguracja IDE może uratować życie

Zapewnienie w IDE, np. VSCode, dostępu do autouzupełniania dokumentacja wybranego narzędzia testowego może zwiększyć Twoją wydajność. Dla: istnieje ponad 100 metod na assert w potwierdzeniu Chai oraz posiadanie dokumentacji wyświetlane w tekście, mogą być wygodne.

Może to być szczególnie ważne w przypadku niektórych platform testowych wypełniających w globalnej przestrzeni nazw i metodami testowania. To subtelna różnica, ale można często korzystać z bibliotek testowych bez ich importowania, jeśli są automatycznie dodane do globalnej przestrzeni nazw:

// some.test.js
test('using test as a global', () => {  });

Zalecamy importowanie pomocników, nawet jeśli są one obsługiwane automatycznie, ponieważ daje to IDE jasny sposób wyszukiwania tych metod. (Być może masz napotkał ten problem podczas budowania React, ponieważ niektóre bazy kodu React – globalne, ale niektóre nie – muszą zostać zaimportowane we wszystkich plikach za pomocą React.)

// some.test.js
import { test } from 'vitest';
test('using test as an import', () => {  });