2010-04-11 6 views
27

Jak napisać testy, gdzie warunki jak powstają następujące:Jak dostarczać wejścia standardowe, pliki i zmienne środowiskowe do testów jednostkowych Pythona?

  1. testu wejść użytkownika.
  2. Wpis testu odczytany z pliku.
  3. Wpis testu odczytany ze zmiennej środowiskowej.

Byłoby wspaniale, gdyby ktoś mógł mi pokazać, jak podejść do wyżej wymienionych scenariuszy; nadal byłoby wspaniale, gdybyś mógł wskazać mi kilka dokumentów/artykułów/wpisów na blogach, które mogłem przeczytać: .

+0

sprawdzenie, czy rozumiem pytanie poprawnie: co chcesz wiedzieć: jak sprawdzić, czy plik wykonywalny/script, biorąc pod uwagę kilka czynników produkcji, takich jak zmienne stdin/Środowisko daje oczekiwanych wyników , np. stdout, stderr czy status wyjścia? –

Odpowiedz

30

Wszystkie trzy opisane sytuacje dotyczą sytuacji, w których należy szczególnie uporać się z zapewnieniem luźnego połączenia w projekcie.

Czy ty naprawdę potrzebujesz do testu jednostkowego Pythona w metodzie raw_input? Metoda open? os.environ.get?

Musisz skonfigurować projekt, aby móc zastąpić inne sposoby pobierania tego wejścia. Następnie, podczas testów jednostkowych, wrzucisz stub, który tak naprawdę nie nazywa się raw_input lub open.

Na przykład, twój normalny kod może być coś takiego:

import os 
def say_hello(input_func): 
    name = input_func() 
    return "Hello " + name 

def prompt_for_name(): 
    return raw_input("What is your name? ") 

print say_hello(prompt_for_name) 
# Normally would pass in methods, but lambdas can be used for brevity 
print say_hello(lambda: open("a.txt").readline()) 
print say_hello(lambda: os.environ.get("USER")) 

Sesja wygląda następująco:

 
What is your name? somebody 
Hello somebody 
Hello [some text] 

Hello mark 

Wtedy nasz test będzie jak:

def test_say_hello(): 
    output = say_hello(lambda: "test") 
    assert(output == "Hello test") 

Należy pamiętaj, że nie powinieneś testować języka IO (chyba że to ty projektujesz język, w hich jest zupełnie inną sytuacją).

+7

+1: To nie tylko sprawia, że ​​kod jest bardziej testowalny, ale także bardziej nadaje się do ponownego użycia, ponieważ nie jest związany z konkretnym źródłem wejściowym. –

+1

Z wyjątkiem sytuacji, gdy nie masz kontroli nad powyższym.Ładna trasa jest często niedostępna. – famousgarkin

+1

W jaki sposób to podejście działa z wieloma wejściami przypisanymi do wielu zmiennych. Czy tworzysz wiele input_funcs lub czy istnieje bardziej elegancki sposób? – TomKivy

5

Jeśli możesz uciec bez użycia zewnętrznego procesu, zrób to.

Istnieją jednak sytuacje, w których jest to skomplikowane i naprawdę chcesz użyć procesu, np. Chcesz przetestować interfejs wiersza poleceń pliku C.

Wprowadzane przez użytkownika

Zastosowanie subprocess.Popen jak w:

process = subprocess.Popen(
    command, 
    shell = False, 
    stdin = subprocess.PIPE, 
    stdout = subprocess.PIPE, 
    stderr = subprocess.PIPE, 
    universal_newlines = True 
) 
stdout, stderr = process.communicate("the user input\nline 2") 
exit_status = process.wait() 

Nie ma różnicy między sobą wejście od użytkownika i biorąc go z rury na wejściu wykonanej metodami jak raw_input lub sys.stdin.read().

Pliki

  • Utwórz katalog tymczasowy i tworzyć pliki, które mają być odczytywane z tam na swoich metod badawczych setUp:

    tdir = tempfile.mkdtemp(
        prefix = 'filetest_', 
    ) 
    fpath = os.path.join(tdir,'filename') 
    fp = open(fpath, 'w') 
    fp.write("contents") 
    fp.close() 
    
  • Czy plik czytania w testach .

  • Następnie usuń katalog tymczasowy.

    shutil.rmtree(tdir) 
    
  • Jest to dość skomplikowany odczyt z plików, a większość programów można odczytać zarówno z plików lub ze standardowego wejścia (na przykład z fileinput). Jeśli więc chcesz przetestować, co dzieje się, gdy wprowadzana jest pewna treść, a twój program akceptuje STDIN, po prostu użyj Popen, aby przetestować program.

Zmienne środowiskowe

  • Ustaw zmienne środowiskowe z os.environ["THE_VAR"] = "the_val"
  • rozbrojony je z del os.environ["THE_VAR"]
  • os.environ = {'a':'b'} nie działa
  • Następnie zadzwonić subprocess.Popen. Środowisko jest dziedziczone z procesu wywoływania.

kod szablonu

Mam moduł na my github która testuje STDOUT, STDERR oraz stan wyjścia danego STDIN, argumenty wiersza polecenia i otoczenie. Sprawdź także testy tego modułu w ramach "testów" reż. Musi istnieć wiele lepszych modułów, więc weź moje tylko dla celów edukacyjnych.

29

Jeśli jesteś przywiązany do używania raw_input (lub dowolnego innego określonego źródła wejścia), jestem wielkim zwolennikiem mock library. Biorąc pod uwagę, że Mark Rushakoff kod stosowany w jego przykład:

def say_hello(): 
    name = raw_input("What is your name? ") 
    return "Hello " + name 

Kod testu można użyć mock:

import mock 

def test_say_hello(): 
    with mock.patch('__builtin__.raw_input', return_value='dbw'): 
     assert say_hello() == 'Hello dbw' 

    with mock.patch('__builtin__.raw_input', side_effect=['dbw', 'uki']): 
     assert say_hello() == 'Hello dbw' 
     assert say_hello() == 'Hello uki' 

Te twierdzenia minie. Zwróć uwagę, że argument side_effect zwraca elementy listy w kolejności. To może zrobić o wiele więcej! Polecam sprawdzenie dokumentacji.

+13

Dla Pythona 3, zwróć uwagę, że '__builtin__' został przemianowany na' builtins', a 'raw_input()' stał się 'input()', czyli: 'mock.patch ('builtins.input', return_value = 'dew')' – geertjanvdk

+3

Mam dbw, a nie rosę :) – dbn

+0

Mock jest częścią rdzeniowego pythona w Pythonie 3 jako 'from unittest import mock'. – dbn

3

Korzystanie pytest:

import os 


def test_user_input(monkeypatch): 
    inputs = [10, 'y'] 
    input_generator = (i for i in inputs) 
    monkeypatch.setattr('__builtin__.raw_input', lambda prompt: next(input_generator)) 
    assert raw_input('how many?') == 10 
    assert raw_input('you sure?') == 'y' 


def test_file_input(tmpdir): 
    fixture = tmpdir.join('fixture.txt') 
    fixture.write(os.linesep.join(['1', '2', '3'])) 
    fixture_path = str(fixture.realpath()) 
    with open(fixture_path) as f: 
     assert f.readline() == '1' + os.linesep 


def test_environment_input(monkeypatch): 
    monkeypatch.setenv('STAGING', 1) 
    assert os.environ['STAGING'] == '1' 
Powiązane problemy