Testy jednostkowe w .NET Micro Framework

02.04.2013

.NET Micro Framework to framework z rodziny .NET służący do oprogramowywania systemów typu embedded z ograniczonymi zasobami pamięci. Minimalne wymagania to 256 kB pamięci flash oraz 64 kB pamięci RAM. .NET Micro to taki młodszy brat .NET Framework z bardzo okrojonym BCL, WPF i WCF (wersja 4.x).

Jeżeli programowałeś wcześniej w “pełnym dotnecie”, przesiadka na Micro może okazać się szokiem. Niby to ten sam C#, jednak braki w BCL (wynikające z ograniczeń platformy sprzętowej) szybko dają się we znaki. Rzeczy tak oczywiste jak generyki, StringBuilder (od 4.2), operacja na łańcuch znaków, tablicach, strumieniach, wyrażenia regularne (od 4.2) czy LINQ tutaj nie istnieją lub istnieją w bardzo okrojonej formie przypominając swoje “pełne” odpowiedniki praktycznie tylko z nazwy.

Po przyzwyczajeniu się do ograniczeń frameworka i dopisaniu/wygooglowaniu kilku brakujących klas narzędziowych można zacząć w miarę normalnie funkcjonować w tym środowisku… aż do momentu próby przetestowania kodu. Na dzień dzisiejszy żaden z frameworków do testów jednostkowych nie wspiera .NET Micro Framework. Na szczęście dzięki prostemu zabiegowi można objeść to ograniczenie.

Aby pisać testy jednostkowe dla kodu .NET Micro Framework należy wykonać następujące czynności:

  1. Stworzyć normalny projekt z testami (class library + ulubiony framework do testów jednostkowych).
  2. Do projektu z testami dodać istniejące pliki klas z projektu .NET Micro, które chcemy przetestować. Podczas wyboru pliku należy wybrać Add As Link.

 

image 

W projekcie testowym powstaną linki do oryginalnych plików. Kompilacja wygeneruje kod dołączonych do projektu klas w docelowej wersji .NET Framework, określonej dla projektu testowego. Jeżeli w testowanych (dołączanych) plikach używamy kodu specyficznego dla .NET Micro (np. z namespace Microsoft.SPOT), to taki plik nie skompiluje się. Można próbować sobie poradzić z tym problemem przy pomocy dyrektywy #if/#else. Przykład użycia:

#if MF_FRAMEWORK_VERSION_V4_2
using Microsoft.SPOT;
#endif

if (calculatedCrc != msgCrc)
{
        var s = bytes.ToByteString();
#if MF_FRAMEWORK_VERSION_V4_2
        Debug .Print(s);
#endif
        throw new InvalidCrcException( "Invalid crc" );
}

Dzięki takiemu można newralgiczne kawałki kodu zamienić na odpowiedniki z .NET Framework (np. Debug.Print na Console.WriteLine) i uruchomić kod testu jednostkowego.

Ten sposób ma również swoje ograniczenia. Nie da się wszystkiego zamienić na odpowiedniki z pełnego .NET, bo czasami zwyczajnie ich nie ma. Przykładem mogą być tutaj klasy InputPort, PWM, I2C czy inny specyficzny dla .NET Micro kod dotyczący warstwy sprzętowej. Warto się wtedy zastanowić nad wprowadzeniem kolejnego poziomu abstrakcji i opakowaniu tych obiektów odpowiednimi klasami i udostępnieniu interfejsu, który będziemy mogli zaślepić przy użyciu NSubstitute czy Moq.