2015-02-24 11 views
10

Mam metodę w python (2.7), która robi foo, i poddaje się po 5 minutach, jeśli foo nie działa.przesłonić zmienną lokalną pytona w unittest

def keep_trying(self): 
    timeout = 300 #empirically derived, appropriate timeout 
    end_time = time.time() + timeout 
    while (time.time() < end_time): 
     result = self.foo() 
     if (result == 'success'): 
      break 
     time.sleep(2) 
    else: 
     raise MyException('useful msg here') 

Znam niektóre możliwe wyniki z foo(), więc używam makiety do sfałszowania tych wartości zwracanych. Problem polega na tym, że nie chcę, aby test był uruchamiany 5 minut przed wyświetleniem wyjątku.

Czy istnieje sposób na zastąpienie tej lokalnej wartości limitu czasu? Chciałbym, żeby to było tylko kilka sekund, abym mógł zobaczyć pętlę kilka razy, a następnie poddać się i podnieść.

Następujące nie działa:

@patch.object(myClass.keep_trying, 'timeout') 
@patch.object(myClass, 'foo') 
def test_keep_trying(self, mock_foo, mock_timeout): 
    mock_foo.return_value = 'failed' 
    mock_timeout.return_value = 10 # raises AttributeError 
    mock_timeout = 10 # raises AttributeError 
    ... 

Odpowiedz

11

Zamiast starać się wyśmiewać wartość jeśli timeout, będziemy chcieli, aby drwić wartość zwracaną time.time().

np.

@patch.object(time, 'time') 
def test_keep_trying(self, mock_time): 
    mock_time.side_effect = iter([100, 200, 300, 400, 500, 600, 700, 800]) 
    ... 

Teraz po raz pierwszy time.time() nazywa, dostaniesz wartość 100, więc powinien limitu czasu, gdy po kilku turach swojej pętli while. Możesz także kpić z wersji time.sleep i liczyć ile razy zostanie wywołana, aby upewnić się, że część kodu działa poprawnie.


Innym podejściem (które nie jest całkowicie prostopadłe do tego powyżej) jest umożliwienie użytkownikowi przejść opcjonalnego słowa kluczowego limitu czasu dla funkcji:

def keep_trying(self, timeout=300): 
    ... 

ten pozwala określić czas oczekiwania cokolwiek chcesz w testach (iw przyszłym kodzie, który nie chce czekać 5 minut ;-).

+0

mock_time to jest! – anregen

+0

Właściwie użyłem '@ patch.object (myClass.time, 'time')', aby zachować mock.side_effect lokalnie do tego jednego modułu. – anregen

11

Nie możesz sfałszować lokalnej zmiennej funkcji. Aby łatwiej swój kod, aby przetestować, zmień go, na przykład:

def keep_trying(self, timeout=300): 
    end_time = time.time() + timeout 
    # etc, as above 

więc staje się trywialne do testów uruchomić go z krótszego czasu oczekiwania!

+0

I właśnie edytowałem to w mojej odpowiedzi ... :-) – mgilson

+3

Oryginalna funkcja jest częścią opublikowanego interfejsu API, więc nie chcę zmieniać podpisu i nie chcę, aby użytkownicy mieli dostęp do dostosuj ten limit czasu. – anregen

+3

Python działa jako dżentelmeńska umowa i niektóre konwencje - jeśli argument zostanie opatrzony jednym wiodącym podkreśleniem - użytkownicy będą wiedzieć, że argument nie powinien być używany - a każdy przyzwoity system dokumentacji pozwoli ci pominąć prywatne argumenty, metody, pola itp tak, więc tylko bardzo zdeterminowany użytkownik znajdzie argument w pierwszej kolejności. –

Powiązane problemy