Na czym polega testowanie

Testując oprogramowanie, możesz sprawdzić, czy działa ono prawidłowo. Testowanie definiuje się ogólnie jako proces uruchamiania oprogramowania w określony sposób w celu zapewnienia jego poprawnego działania.

Pomyślne testy dadzą Ci pewność, że gdy dodasz nowy kod i funkcje, a nawet zaktualizujesz zależności, napisane oprogramowanie będzie nadal działać zgodnie z oczekiwaniami. Testowanie może również pomóc chronić oprogramowanie przed mało prawdopodobnymi scenariuszami lub nieoczekiwanymi danymi wejściowych.

Oto kilka przykładów zachowań w internecie, które warto przetestować:

  • Zadbanie o to, aby funkcje witryny działały prawidłowo po kliknięciu przycisku.
  • Upewnienie się, że funkcja złożona daje prawidłowe wyniki.
  • Wykonanie działania, które wymaga zalogowania się przez użytkownika.
  • Sprawdzanie, czy formularz prawidłowo zgłasza błąd po wprowadzeniu nieprawidłowych danych.
  • Zadbanie o to, żeby złożona aplikacja internetowa mogła nadal działać, gdy użytkownik ma bardzo małą przepustowość lub przechodzi w tryb offline.

Testowanie automatyczne i ręczne

Możesz testować swoje oprogramowanie na 2 ogólne sposoby: testy automatyczne i testy ręczne.

Testy ręczne polegają na bezpośrednim uruchomieniu oprogramowania, np. przez wczytanie witryny w przeglądarce i sprawdzeniu, czy działa ona zgodnie z oczekiwaniami. Testy ręczne są łatwe do utworzenia lub zdefiniowania – na przykład czy Twoja strona się wczytuje? Potrafisz wykonać te działania? Ale każde uruchomienie pochłania mnóstwo czasu przez człowieka. Chociaż ludzie są bardzo kreatywni, co umożliwia przeprowadzanie takich typów testów, tzw. testów eksploracyjnych, wciąż możemy nie dostrzegać błędów i niespójności, zwłaszcza gdy wielokrotnie wykonują to samo zadanie.

Testowanie automatyczne to dowolny proces, który umożliwia kodowanie testów i wielokrotne ich przeprowadzanie na komputerze w celu potwierdzenia zamierzonego działania oprogramowania bez powtarzania tych czynności przez człowieka, takich jak konfigurowanie czy sprawdzanie wyników. Co ważne, po skonfigurowaniu testów automatycznych można je często uruchamiać. Jest to nadal bardzo szeroka definicja i należy podkreślić, że testy automatyczne mogą przybierać różne formy. Większość kursu dotyczy automatycznych testów.

Testy ręczne mają swoje miejsce – często jako wstęp do testów automatycznych, ale także wtedy, gdy testy automatyczne stają się zbyt zawodne, mają szeroki zakres lub są nieporęczne, aby je zapisać.

Podstawy z przykładem

Dla nas, programistów stron internetowych, którzy piszą JavaScript i języki pokrewne, zwięzły automatyczny test może być skryptem, który uruchamia się codziennie, np. w węźle lub w przeglądarce:

import { fibonacci } from "../src/math.js";

if (fibonacci(0) !== 0) {
  throw new Error("Invalid 0th fibonacci result");
}
const fib13 = fibonacci(13);
if (fib13 !== 233) {
  throw new Error("Invalid 13th fibonacci result, was=${fib13} wanted=233");
}

To uproszczony przykład, który przedstawia te statystyki:

  • Jest to test, ponieważ uruchamia oprogramowanie (funkcja Fibonacci) i sprawdza, czy jego działanie działa w oczekiwany sposób, porównując jego wyniki z oczekiwanymi wartościami. Jeśli działanie jest niepoprawne, powoduje to błąd, który JavaScript wyraża, przesyłając funkcję Error.

  • Nawet jeśli uruchamiasz ten skrypt ręcznie w terminalu lub w przeglądarce, jest to wciąż test automatyczny, bo można go uruchamiać wielokrotnie bez konieczności wykonywania poszczególnych czynności. Więcej informacji znajdziesz na następnej stronie, gdzie przeprowadzane są testy.

  • Mimo że ten test nie korzysta z żadnych bibliotek – to JavaScript, który można uruchomić wszędzie – nadal jest to test. Jest wiele narzędzi, które ułatwiają pisanie testów, w tym te, które omówimy w dalszej części tego szkolenia. Wszystkie jednak działają zgodnie z podstawową zasadą wywoływania błędów w razie niepowodzenia.

Testowanie bibliotek w praktyce

Większość bibliotek lub wbudowanych platform do testowania zawiera 2 główne elementy podstawowe, które ułatwiają pisanie testów: assercje i sposób definiowania niezależnych testów. Zostaną one szczegółowo omówione w następnej sekcji – assercjach i innych elementach podstawowych. Ogólnie jednak należy pamiętać, że niemal wszystkie testy, które oglądasz i piszesz, będą korzystać z tego rodzaju podstawowych elementów.

Asercje to sposób na połączenie sprawdzania wyniku i wywoływanie błędu, gdy coś pójdzie nie tak. Aby na przykład zwiększyć zwięzłość poprzedniego testu, wprowadź assert:

import { fibonacci } from "../src/math.js";
import { assert } from "a-made-up-testing-library";

assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");

Możesz jeszcze bardziej ulepszyć ten test, definiując niezależne testy, opcjonalnie pogrupowane w pakiety. Ten pakiet niezależnie testuje funkcję Fibonacci i katalońską:

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

suite("math tests", () => {
  test("fibonacci function", () => {
    assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
    assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
  });
  test("relationship between sequences", () => {
    const numberToCheck = 4;
    const fib = fibonacci(numberToCheck);
    const cat = catalan(numberToCheck);
    assert.isAbove(fib, cat);
  });
});

W tym kontekście testowania oprogramowania test odnosi się do przypadku testowego: pojedynczego, niezależnego, możliwego do rozwiązania sytuacji, np. „relacji między sekwencjami” w poprzednim przykładzie.

Testy z nazwami własnymi przydają się między innymi do tych zadań:

  • Ustalanie czasu powodzenia lub niepowodzenia testu.
  • Przedstaw błąd lub scenariusz po nazwie, aby ułatwić sprawdzenie, czy scenariusz został rozwiązany.
  • przeprowadzanie niektórych testów niezależnie od innych, na przykład przy użyciu filtra globalnego.

Przykładem może być korzystanie z trzech aspektów testowania jednostkowego: rozmieszczanie, działanie i twierdzenie. Każdy przypadek testowy:

  • Rozmieść wartości lub stan (mogą to być po prostu dane wejściowe zakodowane na stałe).
  • Wykonaj działanie, na przykład wywołaj metodę.
  • Zastosuj wartości wyjściowe lub zaktualizowany stan (za pomocą assert).

Skala testów

Przykładowy kod w poprzedniej sekcji opisuje test jednostkowy, ponieważ testuje niewielkie części oprogramowania, często koncentrując się na jednym pliku, a w tym przypadku – tylko na wynikach działania pojedynczej funkcji. Złożoność testów rośnie, gdy weźmiesz pod uwagę kod z wielu plików, komponentów, a nawet różnych połączonych ze sobą systemów (czasami pozostających poza Twoją kontrolą, np. w przypadku usługi sieciowej lub zależności zewnętrznej). Z tego względu typy testów są często nazwane zgodnie z ich zakresem lub skalą.

Oprócz testów jednostkowych inne typy testów to m.in. testowanie komponentów, testowanie wizualne i testowanie integracji. Żadna z tych nazw nie ma ścisłych definicji i może mieć różne znaczenie w zależności od bazy kodu. Pamiętaj, by potraktować je jako wskazówki i opracować definicje, które będą Ci odpowiadać. Jaki jest na przykład komponent w trakcie testowania w systemie? W przypadku deweloperów React może to być zmapowane na „komponent reakcji”, ale w pozostałych kontekstach może mieć inne znaczenie dla programistów.

Skala pojedynczego testu może zostać umieszczona w koncepcji nazywanej „piramidą testową”. W ten sposób można ogólnie określić, co i w jaki sposób przebiega w ramach konkretnego testu.

Piramida testowania z kompleksowymi testami E2E u góry, testami integracji na środku i testami jednostkowymi na dole.
Piramida testowania.

Pomysł ten był wielokrotnie powtarzany i obecnie spopularyzowano inne kształty, takie jak testowy diament czy stożek lodowy. Priorytety w pisaniu testów będą prawdopodobnie zależeć od Twojej bazy kodu. Popularną funkcją jest jednak to, że prostsze testy, takie jak testy jednostkowe, zwykle są szybsze i łatwiejsze do pisania (dzięki czemu będzie ich więcej) oraz że testujesz ograniczony zakres. Złożone testy, takie jak testy kompleksowe, są trudne do napisania, ale mogą obejmować szerszy zakres. W przypadku wielu „kształtów” najważniejszym elementem testowania są zwykle testy ręczne, ponieważ niektóre interakcje użytkowników są zbyt złożone, aby można było je przekształcić w test automatyczny.

Te typy zostaną rozszerzone w sekcji Typy testów automatycznych.

Sprawdź swoją wiedzę

Jakie elementy podstawowe zawiera większość bibliotek i platform testowych?

Usługa biegowa wykorzystująca dostawcę chmury.
Niektóre programy do biegania działające w przeglądarce umożliwiają zlecanie testów firmie zewnętrznej, ale nie jest to normalna funkcja bibliotek testowania.
Asercje, które powodują wyjątki, jeśli nie są spełnione.
Chociaż wynik testu nie powiódł się, możesz zgłosić błąd, ale assert() i jego odmiany są zwykle uwzględniane, ponieważ ułatwiają one pisanie testów.
Sposób klasyfikowania testów w piramidę testowania.
Nie ma standardowego sposobu na to. Możesz poprzedzać nazwy testów lub umieszczać je w różnych plikach, ale kategoryzowanie nie jest w rzeczywistości wbudowane w większość platform testów.
Możliwość definiowania niezależnych testów według funkcji.
Metoda test() jest dostępna w prawie wszystkich uruchamiach testów. Jest to ważne, ponieważ kod testowy nie działa na najwyższym poziomie pliku, przez co mechanizm uruchamiający test może traktować każdy przypadek testowy jako niezależną jednostkę.