2010-04-20 11 views
6

Próbowałem rozpocząć pracę z testowaniem jednostkowym podczas pracy nad małym programem Cli.W jaki sposób należy przepisać bazę danych execute/commit mojej bazy danych, aby umożliwić jej testowanie jednostkowe?

Mój program w zasadzie analizuje argumenty i opcje wiersza poleceń i decyduje, którą funkcję wybrać. Każda z funkcji wykonuje pewną operację na bazie danych.

Tak więc, na przykład, może mam funkcji Utwórz:

def create(self, opts, args): 
    #I've left out the error handling. 
    strtime = datetime.datetime.now().strftime("%D %H:%M") 
    vals = (strtime, opts.message, opts.keywords, False) 
    self.execute("insert into mytable values (?, ?, ?, ?)", vals) 
    self.commit() 

Gdyby moja próba przypadek wywołać tę funkcję, a następnie wykonać select sql, aby sprawdzić, że wiersz został wprowadzony? Brzmi to rozsądnie, ale sprawia też, że testy są trudniejsze do utrzymania. Czy przepisałbyś funkcję, by zwrócić coś i sprawdzić wartość zwracaną?

Dzięki

+0

Myślę, że testowanie jednostkowe to dwa słowa. –

Odpowiedz

8

Odpowiedź Alex obejmuje podejście polegające na wstrzyknięciu zależności. Innym jest faktorowanie swojej metody. W obecnej formie ma dwie fazy: skonstruowanie instrukcji SQL i wykonanie instrukcji SQL. Nie chcesz testować drugiej fazy: nie napisałeś silnika SQL ani bazy danych, możesz założyć, że działają poprawnie. Faza 1 to twoja praca: skonstruowanie instrukcji SQL. Więc można ponownie zorganizować kod, dzięki czemu można przetestować tylko Faza 1:

def create_sql(self, opts, args): 
    #I've left out the error handling. 
    strtime = datetime.datetime.now().strftime("%D %H:%M") 
    vals = (strtime, opts.message, opts.keywords, False) 
    return "insert into mytable values (?, ?, ?, ?)", vals 

def create(self, opts, args): 
    self.execute(*self.create_sql(opts, args)) 
    self.commit() 

Funkcja create_sql jest faza 1, a teraz jest wyrażona w sposób, który pozwala pisać testy bezpośrednio przed nim: to przyjmuje wartości i zwraca wartości, dzięki czemu możesz napisać testy jednostkowe obejmujące ich funkcjonalność. Sama funkcja create jest teraz prostsza i nie musi być tak wyczerpująco testowana: możesz mieć kilka testów, które pokazują, że naprawdę poprawnie wykonuje on SQL, ale nie musisz zajmować się wszystkimi skrajnymi przypadkami w create.

BTW: This video from Pycon (Tests and Testability) może być interesujące.

6

pewno byłaby ten sposób łatwość testowania - na przykład, wtrysk zależność może pomóc:

def create(self, opts, args, strtime=None, exec_and_commit=None): 
    #I've left out the error handling. 
    if strtime is None: 
     strtime = datetime.datetime.now().strftime("%D %H:%M") 
    vals = (strtime, opts.message, opts.keywords, False) 
    if exec_and_commit is None: 
     exec_and_commit = self.execute_and_commit 
    exec_and_commit("insert into mytable values (?, ?, ?, ?)", vals) 

to oczywiście zakłada, że ​​masz sposób execute_and_commit który wywołuje execute a następnie commit metody.

W ten sposób kod testowy może wstrzyknąć znaną wartość dla strtime i wstrzyknąć własną wartość dzwonka pod numer exec_and_commit, aby sprawdzić, czy zostanie wywołany z oczekiwanymi argumentami.

0

Nie zna składni Pythona, ale jeśli dopiero zaczynasz testowanie jednostkowe, najprostszym sposobem na rozpoczęcie może być wyodrębnienie metody, którą przekazujesz w argach wiersza poleceń i przywrócenie polecenia sql. W tej metodzie możesz przetestować część kodu, w której znajduje się prawdziwa logika. Przekaż różne typy argumentów i sprawdź wyniki w zależności od tego, jaka powinna być komenda sql. Po rozpoczęciu poznawania smaku testów jednostkowych możesz dowiedzieć się trochę o kpiach i zastrzyku zależności, aby sprawdzić, czy poprawnie wywołujesz funkcje, które aktualizują db.

Mam nadzieję, że są bardziej zaznajomieni z java C# składni niż ja ze składnią Pythona :)

public string GetSqlCommand(string[] commandLineArgs) 
{ 
    //do your parsing here 
    return sqlCommand; 
} 
[Test] 
public void emptyArgs_returnsEmptySqlCommand() 
{ 
    string expectedSqlCommand=""; 
    assert.AreEqual(expectedSqlCommand, GetSqlCommand(new string[]) 
} 
3

Generalnie chcesz mieć testy jednostkowe w miejscu przed refaktoringu, aby zapewnić żadnego rozerwania zmian. A jednak może być konieczna refaktoryzacja, aby umożliwić testowalność ...niefortunny paradoks, który mnie ugryzł.

To powiedziawszy, istnieje kilka małych refaktoryzacji, które można wykonać bezpiecznie, bez zmiany zachowania. Zmień nazwę i wypakuj są dwa.

Polecam przyjrzeć się książce Michaela Feathersa, Praca ze starszym kodem. Koncentruje się na kodzie refaktoryzacji pod kątem testowalności. Przykłady znajdują się w Javie, ale koncepcje miałyby zastosowanie równie dobrze do Pythona.

+0

i kod C++ jest używany w książce – Gutzofter

Powiązane problemy