Chcesz zacząć swoją przygodę z Test Driven Development w systemach embedded? A może szukasz rozwiązania jakiś specyficznych problemów? TDD ogólnie i wdrożenie go w pracach nad systemami embedded są jednymi z głównych tematów, jakie poruszam na tym blogu. Ta strona jest dobrym punktem startowym, gdzie znajdziesz parę słów wprowadzenia oraz linki do najważniejszych artykułów o TDD Embedded, a także do polecanych materiałów zewnętrznych.

Dlaczego TDD Embedded?

Branża embedded skutecznie opiera się nowinkom technicznym, które podbijają inne gałęzie programowania. Jeszcze niedawno dominującym trendem było rozwijanie softu przez pojedyncze osoby, które dodatkowo pełnią również rolę elektroników i testerów. Poprawność napisanego kodu zwykle testowali manualnie bezpośrednio na sprzęcie. Ostatnio sytuacja zaczyna się zmieniać, ale nadal takie podejście jest bardzo powszechne.

Producenci procesorów i narzędzi nie ułatwiają zadania. Kompilatory często nie wspierają nowych funkcji języka, albo mają swoje własne dialekty. A dedykowane IDE są dosyć toporne i wydają się wspierać jedynie testowanie na sprzęcie. Często trudna jest nawet współpraca kilku osób nad jednym projektem. Przez to rozwój softu jest dosyć powolny, a jakość pozostawia wiele do życzenia.

Dzięki TDD możemy dużą część developmentu uniezależnić od docelowego sprzętu i dedykowanych IDE. Możemy korzystać z wszystkich dobrodziejstw programowania na PC łącznie z odpalaniem na nim kodu, debugowaniem i używaniem tooli. TDD Embedded poza wszystkimi zaletami klasycznego TDD ma jeszcze jeden wielki plus – pozwala uniezależnić się od sprzętu. W końcu duża część softu nie wchodzi w bezpośrednią interakcje z hardwarem i może być rozwijana niezależnie.

Moje doświadczenie z TDD embedded

Pierwszy raz usłyszałem o TDD w 2012 roku. Szukałem wtedy jakiegoś systemowego podejścia pozwalającego ograniczyć ilość błędów i uwolnić mnie od manualnego testowania na sprzęcie coraz większego projektu. Znalazłem trochę informacji w internecie, przeczytałem książkę Jamesa Grenninga i wiedziałem, że to rozwiązanie moich problemów. Od tego czasu stosowałem TDD w różnych rodzajach systemów: sterowniki przemysłowe, IoT, inteligentne budynki, czy systemy safety-critical.  Wielokrotnie takie podejście udowodniło swoją przydatność zwiększając niezawodność komponentów, przyspieszając integrację, umożliwiając zmiany i pozwalając szybko zidentyfikować braki w wymaganiach.

Przez lata używania TDD w systemach embedded miałem styczność z różnymi niestandardowymi problemami i zebrałem sporo doświadczenia. Na tym blogu staram się dzielić zdobytą wiedzą. Najważniejsze artykuły dotyczące zarówno TDD w ogóle, jak i problemów specyficznych dla embedded można znaleźć w dwóch kolejnych sekcjach.

Wprowadzenie do Test Driven Development

Zbiór najważniejszych informacji o TDD przydatnych dla każdego developera (nie tylko embedded):

  • Dlaczego zainteresowałem się TDD? – wady klasycznego podejścia, czyli testowania manualnego po napisaniu większej ilości kodu.
  • Na czym polega TDD – omówienie głównych założeń TDD. Na początku pisanie testu przed implementacją i działanie w krótkich iteracjach wydaje się nieintuicyjne. Trzeba poświęcić trochę czasu, aby nabrać wprawy.
  • Czym właściwie jest unit test i czym różni się od testów innych poziomów?  – definicja unit testu i jego miejsce na piramidzie testów. Co jesteśmy w stanie sprawdzić unit testem, a co musimy sprawdzić na innych poziomach?
  • Zalety TDD – zalety TDD wykraczają daleko poza lepsze pokrycie testami naszego kodu. TDD wpływa również na decyzje architektoniczne i ma wartość dokumentacyjną.
  • Wymówki, aby nie pisać unit testów – osoby, które nie znają TDD zwykle mają ten sam zestaw argumentów przeciw. Głównym jest oczywiście zwiększenie czasu developmentu. W artykule tłumaczę, dlaczego te argumenty nie są prawdziwe.
  • Jak pisać dobre unit testy – zestaw dobrych praktyk pozwalających pisać przydatne i łatwe w utrzymaniu testy.
  • Mocki – radzenie sobie z zależnościami w testach – kompendium wiedzy o mockach. Odpowiadam między innymi na pytania kiedy stosować, jakie mamy rodzaje mocków, czy używać gotowych frameworków.
  • Miary jakości unit testów – jak ocenić, czy nasze testy są dobre? Do czego możemy wykorzystać code coverage i jakie są najczęstsze nieporozumienia związane z tą metryką.
  • Antywzorce unit testów – czego powinniśmy unikać używając TDD i pisząc unit testy?
  • Kiedy nie stosować TDD – TDD, mimo swojej użyteczności, posiada pewne ograniczenia. Warto je znać i wiedzieć, kiedy pisanie zgodnie z TDD nie przyniesie nam żadnej wartości.

Test Driven Development w systemach embedded

W tej serii omawiam problemy i techniki specyficzne dla systemów embedded:

  • Korzyści z TDD w systemach embedded – TDD w systemach embedded jest szczególnie przydatne, ponieważ możemy ograniczyć do minimum użycie hardware’u i związane z nim problemy. Zamiast tego większość developmentu odbywa się używając jedynie maszyny developerskiej.
  • Proces TDD dla systemów embedded – aby zapewnić, że kod pisany w TDD na maszynie developerskiej działa bez problemu na sprzęcie potrzebne są pewne dodatkowe kroki. Muszą się one stać częścią procesu wytwarzania oprogramowania.
  • TDD embedded -system buildowania – jak powinien wyglądać system buildowania, aby sprostać wyzwaniom stawianym przez proces opisany w poprzednim artykule?
  • Unity – framework testowy w C – opis działania prostego frameworka w C zaprojektowanego z myślą o systemach embedded. Można go bez problemu uruchamiać zarówno na PC, jak i na docelowym sprzęcie.
  • CppUTest – framework testowy w C++ – kolejny framework do embedded, tym razem napisany w C++. Jeżeli nasza docelowa platforma posiada kompilator C++, będziemy w stanie bez problemu uruchomić na niej testy.
  • Pisanie własnych mocków – czasem warto pisać własne mocki zamiast używać frameworka, w artykule tłumaczę dlaczego. Poza tym pokazuję przykładowe implementacje w C i C++.
  • CppUMock – framework do mockowania (planowany artykuł)
  • Googletest – rozbudowany framework testowy w C++ (planowany artykuł)
  • Googlemock – rozbudowany framework do mockowania (planowany artykuł)
  • Jak testować funkcje prywatne i statyczne? – testowanie szczegółów implementacyjnych niebędących częścią publicznego API nie jest dobrą praktyką. Jednak czasem jesteśmy do tego zmuszeni. W artykule omawiam kilka przydatnych technik radzenia sobie z prywatnymi funkcjami.
  • Jak testować nieskończone pętle? – w embedded nieskończone pętle są używane stosunkowo często. Mogą one być częścią ważnego mechanizmu bezpieczeństwa aktywowanego w przypadku ciężkiego błędu. Dlatego powinniśmy być w stanie je przetestować. W artykule opisuję jak to zrobić.

Materiały zewnętrzne