Testy jednostkowe

Testy jednostkowe to mini programiki, które mają na celu sprawdzenie poprawności działania pojedynczej jednostki kodu (funkcji, czy pojedynczej klasy). Pojedynczej (małej) dlatego, że jeśli wykryją nieprawidłowość to podejrzany obszar poszukiwań buga jest odpowiednio mały. Można sobie porównać z uruchomieniem całej aplikacji i stwierdzeniem, że nie działa wtedy błąd może być wszędzie.

Ręczne testowanie

Tak naprawdę to jest to zwykła aplikacja C++ z własnym mainem, tylko w przypadku testów main ma za zadanie odpalić wszystkie zarejestrowane testy w dowolnej kolejności i zaraportować wynik jeśli zostały wykryte błędy.

Implementacja w kodzie pewnej niezwykle przydatnej funkcjonalności:

Factorial.h
#ifndef FACTORIAL_H_
#define FACTORIAL_H_
 
int Factorial(int value);
 
#endif
Factorial.cpp
#include "Factorial.h"
 
int Factorial(int value) {
  if (value <= 0) {
    return 1;
  }
  return value*Factorial(value-1);
}

Ręcznie napisane testy jednostkowe, sprawdzające poprawność implementacji funkcji silnia:

FactorialTest.h
#ifndef FACTORIAL_TEST_H_
#define FACTORIAL_TEST_H_
 
bool FactorialOf0ShouldBe1();
bool FactorialOf1ShouldBe1();
bool FactorialOf10ShouldBe3628800();
bool FactorialOfNegativeShouldBe0();
 
#endif
FactorialTest.cpp
#include "FactorialTest.h"
 
bool FactorialOf0ShouldBe1() {
  int expected = 1;
  int result = Factorial(0);
  return expected == result;
}
 
bool FactorialOf1ShouldBe1() {
  int expected = 1;
  int result = Factorial(1);
  return expected == result;
}
 
bool FactorialOf10ShouldBe3628800() {
  int expected = 3628800;
  int result = Factorial(10);
  return expected == result;
}
 
bool FactorialOfNegativeShouldBe0() {
  int expected = 0;
  int result = Factorial(-7);
  return expected == result;
}

main od testów jednostkowych, który ma na celu uruchomienie wszystkich testów i zaraportowanie o ewentualnych niepowodzeniach:

main.cpp
#include "FactorialTest.h"
 
#include <string>
#include <utility>
#include <iostream>
 
using std::string;
using std::pair;
using std::cerr;
using std::endl;
 
//test function to wskaźnik na funkcję zwracającą bool
using TestFunction = bool (*)();
 
std::vector<pair<string,TestFunction>> all_tests {{"FactorialOf0ShouldBe1", FactorialOf0ShouldBe1}, {"FactorialOf1ShouldBe1", FactorialOf1ShouldBe1}, {"FactorialOf10ShouldBe3628800", FactorialOf10ShouldBe3628800}, {"FactorialOfNegativeShouldBe0", FactorialOfNegativeShouldBe0}};
 
void ReportTest(const string &failed_test_name) {
  cerr << "Test " << failed_test_name << " FAILED" << endl;
}
 
int RunTests(const std::vector<TestFunction> tests) {
  int failed_tests = 0;
  for (auto test : tests) {
    bool result = test.first();
    if (result) {
      continue;
    } else {
      ReportTest(test.second);
      failed_tests++;
    }
  }
  return failed_tests;
}
 
int main() {
  int result = RunTests(all_tests);
  return result;
}

Już jest nieźle - testy są zautomatyzowane, bo teraz możemy odpalać zawsze testy przez skompilowanie kodu i jego uruchomienie. Do tego nie trzeba weryfikować wyjścia w żaden sposób jeśli jest wszystko w porządku. Możemy również dopisywać nowe testy.

Ale pojawia się wiele powtórzonego kodu, nazwę metody testowej trzeba powtórzyć w kilku miejscach, żeby wszystko działało sprawnie, informacje o tym co poszło nie tak są też bardzo uszczuplone. Ale jest na to rozwiązanie gotowe frameworki testowe.

Google Test Framework

GTesty używają do specyfikacji testu makr języka C, dzięki temu przy definicji nowego przypadku testowego:

FactorialTest.cpp
#include <Factorial.h>
 
TEST(FactorialTest, FactorialOf0ShouldBe1) {
  EXPECT_EQ(1, Factorial(1));
}

Od razu następuje rejestracja metody testowej w strukturze przechowującej uchwyty do testów. Automatycznie następuje zamiana nazw na łańcuchy znaków w celu przygotowania czytelnej wiadomości dla użytkownika. A metodę main można wykorzystać jako domyślnie zaimplementowaną w frameworku, albo napisać ją samemu. Wtedy makro RUN_ALL_TESTS() uruchamia wszystkie testy.

Dodatkową cechą frameworka są dodane specjalne makra do wyrażania naszych oczekiwań co do fragmentów kodu, które w przypadku nie spełnienia oczekiwań produkują czytelniejszą wiadomość dla dewelopera.

Więcej o frameworku można doczytać tutaj: Podstawy GTest Zaawansowane GTesty

pl/dydaktyka/jimp2/2017/labs/unit-testing.txt · ostatnio zmienione: 2017/07/17 08:08 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0