niedziela, 14 kwietnia 2013

Test ma testować, nie powielać kod metody.

czy aby oczywiste jest to wszystko?

Niby jasne, że właśnie tak to powinno wyglądać, ale naprawdę często (dobra, dla ścisłości - częściej niżbym chciał:) zdarza mi się oglądać testy, w których ciele mam dokładną kopię kodu, który znajduje się wewnątrz metody testowanej.
Zdaję sobie sprawę, że w przypadku niektórych rzeczy dużo wygodniej jest wykonać kilka instrukcji niż zastanowić się jaki może być wynik. Najczęściej, takie mam przeświadczenie, programiści decydują się na kopiowanie kodu z metody po to, aby się przypadkiem nie pomylić i żeby oczekiwany wynik był na pewno dobry.

I w tym miejscu pragnę wrzucić delikatne, ale jakże wymowne - WTF?!

czynności kuszące, ale nieporządane

To oczywiste, że gdy mam sprawdzić wynik jakiegoś działania to lepiej mi je napisać, wykonać (aby wyeliminować potencjalny błąd ludzki) i porównać wyniki, lepiej jest wykonać wyrażenie logiczne czy też szereg instrukcji. Tylko czy powtórzenie działań, które zostały wykonane wewnątrz metody nie sprawia przypadkiem, że nie testujemy niczego?
Testy mają na celu porównanie oczekiwanego wyniku z wynikiem, który otrzymamy po wywołaniu konkretnej metody, a czy w momencie, gdy wykonujemy te same obliczenia w metodzie testowej oraz metodzie testowanej, to czy mamy jakiś oczekiwany wynik? Czy może sprowadza się to jedynie do sprawdzenia, czy przy tych samych danych wejściowych metoda zwraca ten sam wynik?

lecz czymże gadanie bez przykładu?

Popatrzcie na poniższy kod:
<?php
class TemperatureUnits
{
  public static function celsjuszToKelwin($temperature)
  {
     return $temperature - 273;
  }
}

Prosty? I wyobraźmy sobie, że programista napisał do niego następujący test:
<?php
class TemperatureUnitsTest
{
  /**
   * @dataProvider temperatureProvider
   * @test
   */
  public function celsjuszToKelwin($temperature)
  {
     $this->assertEquals($temperature - 273, TemperatureUnits::celsjuszToKelwin($temperature));
  }

  /** provider should be here:) */
}

I wszystko świetnie. Działa, test przechodzi i każdy jest zadowolony.
Do momentu kiedy okaże się, że stopnie były przeliczane w niepoprawny sposób:)

Oczywiście naprawa błędu będzie prosta i jest szansa, że zostanie szybko znaleziony. Jednak wyobraźcie sobie, gdy obliczenia będą dużo bardziej skomplikowane, a programista większość testów przeprowadza w ten sposób. Błąd nie zostanie wyłapany w trakcie testów, bo kod metody zostanie przekopiowany. Tworzenie dalszych testów w ten sposób utrudni późniejsze jego odnalezienie. I... problem gotowy.

that's all, folks

Dlatego nie testujcie metod kodem, który jest wewnątrz nich. Przecież wiecie jaki ma być wynik, wiecie czego się spodziewać.
TDD pomaga wyeliminować powyższe problemy, ponieważ przed tym nim powstanie kod, skupiacie się na teście. Nie ma możliwości napisać takiego testu przy użyciu kodu z ciała metody... bo on jeszcze nie istnieje:)