Testy ABLUnit

Testowanie jest nieodłącznym elementem budowania aplikacji. Istnieją różne metody, które mają dać pewność deweloperom, że aplikacja poradzi sobie w każdej sytuacji, bez względu na podawane dane a interfejs będzie tzw. “idiotoodporny”. My skupimy się tutaj tylko na pierwszej części czyli logice aplikacji. I znowu, ten element można testować na różne sposoby np. wykorzystując debugger, kompilację kodu z opcją XREF, czy komunikaty zrzucające dane do plików na dysku. Jednakże te metody mają wadę, polegającą na testowaniu jednej sytuacji na raz, a my chcielibyśmy zautomatyzować testowanie dla różnych sytuacji i różnych wprowadzanych danych.
Do tego celu bardzo dobrze nadaje się testowanie tzw. ABLUnit, dla którego w Developer’s Studio jest zdefiniowany specjalny typ projektu. Testy te zostały wprowadzone od wersji OpenEdge 11.4 i mają zastosowanie dla kodu ABL zarówno do procedur jak i klas.

Na początku musimy przyswoić sobie pewne pojęcia związane z testowaniem.

Przypadek testowy (test case) odnosi się do pliku procedury ABL lub pliku klasy. Przypadek testowy może zawierać wiele testów (zestaw testów).

Test to wewnętrzna procedura w pliku procedury lub metoda w pliku klasy, oznaczona adnotacją @Test.

Asercje (assertions) – służą do określenia, czy dany test powiódł się czy nie. ABLUnit zapewnia metody asercji dla wszystkich typów danych ABL. Sukces lub niepowodzenie tych twierdzeń jest śledzone przez platformę i można je wyświetlić po zakończeniu przebiegu testu.

Zestaw testów (test suite) to zbiór przypadków testowych. Służy on do organizowania i uruchamiania przypadków testowych w grupach logicznych. Zestaw testów może zawierać dowolną liczbę przypadków testowych.

Poniższy obrazek przedstawia cykl wykonania ABLUnit.

Jak wspomniałem wcześniej, każdy test powinien być opatrzony adnotacją @Test. Testowana procedura lub klasa może mieć dowolną liczbę testów, a każdy test jeden zestaw metod/procedur, które są opatrzone adnotacjami @Setup i @TearDown. Przypadek testowy (test case) może mieć po jednej metodzie/procedurze oznaczonej adnotacjami @Before i @After. @Before jest wykonywana raz przed rozpoczęciem testów i może być wykorzystana np. do podłączenia się do bazy. @After jest natomiast wykonywana po ukończeniu testów i może być wykorzystana np. do odłączenia bazy.
Proces testowania rozpoczniemy od stworzenia projektu typu OpenEdge ABLUnit.

Projekt ten jest podłączony do bazy sports2000. Dodajemy do niej tabelę login, która ma dwa pola: username i password (patrz słownik baz danych poniżej).

W tabeli login wstawiamy jeden rekord o polach jak poniżej. Będzie on reprezentować prawidłowe dane logowania do aplikacji.

create login.
username = "test".
password = "test".

Plik Main.p to nasz testowany moduł aplikacji. Zawiera on prosty kod sprawdzający czy osoba logująca się podała prawidłowe dane, dane nieprawidłowe lub nie podała żadnych danych.
W drugim i trzecim przypadku generowany jest błąd z odpowiednim komunikatem.

/* Main.p */
USING Progress.Lang.AppError FROM PROPATH.
DEFINE VARIABLE mye AS Progress.Lang.AppError NO-UNDO.

PROCEDURE loginProc :
  DEFINE INPUT PARAMETER username AS CHARACTER.
  DEFINE INPUT PARAMETER password AS CHARACTER.
  DEFINE OUTPUT PARAMETER loginResult AS CHARACTER INIT "Fail".
  IF username EQ ? THEN
    DO:
      mye = NEW Progress.Lang.AppError("Username is empty", 200).
      mye:ReturnValue = "Username cannot be empty".
      RETURN ERROR mye.
    END.

  FOR EACH login WHERE login.username = username:
    IF login.password = password THEN
    loginResult = "Success".
    ELSE DO:
      mye = NEW Progress.Lang.AppError("wrong password", 200).
      mye:ReturnValue = "Password is wrong". 
      RETURN ERROR mye.
    END.    
  END.
END PROCEDURE.

Aby uruchomić powyższą procedurę z naszych testów, należy:

  • Połączyć się z bazą danych (@Before).
  • Uruchomić główną procedurę (main.p), używając wskaźnika w trybie PERSISTENT (@Setup).
  • Podać wartości wejściowe (@Test).
  • Uruchomić wewnętrzną procedurę loginProc z parametrami wejściowymi i wyjściowymi (@Test).
  • Sprawdzić wynik testu poprzez asercję (@Test).
  • Usunąć procedurę z pamięci (@TearDown).
  • Odłączyć się od bazy danych (@After).
  • Poniżej znajduje się procedura do przeprowadzenia testów (TestCase.p).

    /* TestCase.p */
    USING OpenEdge.Core.Assert FROM PROPATH.
    
    BLOCK-LEVEL ON ERROR UNDO, THROW.
    
    DEFINE VARIABLE procHandle AS HANDLE NO-UNDO.
    DEFINE VARIABLE uname AS CHARACTER NO-UNDO.
    DEFINE VARIABLE pwd AS CHARACTER NO-UNDO.
    DEFINE VARIABLE res AS CHARACTER NO-UNDO.
    
    @Before.
    PROCEDURE Before:
    CONNECT C:\WrkOpenEdge126\db\sports2000.db -H localhost -S 10005 NO-ERROR.
    END PROCEDURE.
    
    @Setup.
    PROCEDURE setUp:
    RUN main.p PERSISTENT SET prochandle.
    END PROCEDURE.
    
    @Test.
    PROCEDURE validusr: /* Test1 */
    uname = "test".
    pwd = "test".
    RUN loginProc IN procHandle (INPUT uname,INPUT pwd,OUTPUT res) .
    Assert:equals("success", res).
    END PROCEDURE.
    
    @Test.
    PROCEDURE invalidusr1: /* Test2 */
    uname = "ablunit".
    pwd = "test".
    RUN loginProc IN procHandle (INPUT uname,INPUT pwd,OUTPUT res) .
    Assert:equals("success", res).
    END PROCEDURE.
    
    @Test.
    PROCEDURE emptyUsr: /* Test3 */
    uname = ?.
    pwd = "invalid".
    RUN loginProc IN procHandle (INPUT uname,INPUT pwd,OUTPUT res) .
    Assert:equals("success", res).
    END PROCEDURE.
    
    @TearDown.
    PROCEDURE tearDown:
    DELETE PROCEDURE procHandle.
    END PROCEDURE.
    
    @After.
    PROCEDURE tearDownAfterProcedure:
    DISCONNECT sports2000.
    DELETE PROCEDURE THIS-PROCEDURE.
    END PROCEDURE.
    

    Po uruchomieniu procedury TestCase.p pojawi sie plik results.xml.

    Klikamy na niego dwukrotnie a w oknie konsoli, w zakładce ABLUnit pojawią się wyniki naszych testów.

    Zgodnie z naszymi przewidywaniami tylko pierwszy test zakończył się powodzeniem.
    Zachęcam do własnych testów!