2015-10-26 14 views
7

Python jest dla mnie stosunkowo nowym językiem. Unit Testing and Dependency Injection to coś, co robię teraz od jakiegoś czasu, więc jestem zaznajomiony z nim z perspektywy C#.Jak wykonać podstawowy zastrzyk zależności w Pythonie (dla kpiarskich/testowych)

Niedawno napisałem ten kawałek kodu Pythona:

import requests # my dependency: http://docs.python-requests.org/en/latest/ 

class someClass: 
    def __init__(self): 
     pass 

    def __do(self, url, datagram): 
     return requests.post(self, url, datagram) 

I wtedy zdałem sobie sprawę, że właśnie stworzył zakodowane zależność. Bleh.

I rozważał zmianę mojego kodu zrobić „Konstruktor” Dependency Injection:

def __init__(self,requestLib=requests): 
    self.__request = requestLib 

def __do(self, url, datagram): 
    return self.__request.post(self, url, datagram) 

To teraz pozwala mi wstrzyknąć fałszywy/makiety zależność dla dobra Unit Testing, ale nie był pewien, czy to został uznany za Python-ic. Apeluję więc do społeczności Pythona o wskazówki.

Jakie są przykłady Python-ic sposoby wykonywania podstawowych DI (głównie ze względu na pisanie testów jednostkowych, które wykorzystują Mocks/Fakes)?

DODATEK Dla każdego, kto ciekaw odpowiedzi Mock, postanowiłem zadać tutaj pytanie: oddzielny How does @mock.patch know which parameter to use for each mock object?

+2

Należy zauważyć, że '__leading_double_underscore' wywołuje wymazywanie nazw i na ogół należy go unikać. Czy nie byłoby łatwiej [próbować '' wniosków'] (https://docs.python.org/3/library/unittest.mock.html) dla badanego modułu niż wstrzykiwać? – jonrsharpe

+0

Przeczytałem, że _ podkreślenie podwójnego podkreślenia oznaczało metodę jako prywatną. Czy myliłem się? Jeśli tak, jak mam oznaczyć coś jako prywatne? – Pretzel

+0

Czy łatwiej byłoby wyśmiewać prośby o moduł, niż go wstrzykiwać? Nie wiem Nie jestem zaznajomiony ze sposobami robienia rzeczy w Pythonie, dlatego pytam. ;) – Pretzel

Odpowiedz

5

nie rób tego. Po prostu importuj żądania w normalny sposób i używaj ich jak zwykle. Przekazywanie bibliotek jako argumentów dla twoich konstruktorów jest fajną rzeczą do zrobienia, ale nie jest zbyt pytonowe i niepotrzebne dla twoich celów. Aby wyśmiać rzeczy w testach jednostkowych, użyj sztucznej biblioteki. W Pythonie 3 jest zbudowany w bibliotece standardowej

https://docs.python.org/3.4/library/unittest.mock.html

A w Pythonie 2 trzeba go instalować oddzielnie

https://pypi.python.org/pypi/mock

Kod testu będzie wyglądać następująco (przy użyciu Pythona 3 wersja)

from unittest import TestCase 
from unittest.mock import patch 

class MyTest(TestCase): 
    @patch("mymodule.requests.post") 
    def test_my_code(self, mock_post): 
     # ... do my thing here... 
+2

Próbuję przetworzyć twój kod. Do czego służy "@patch (" mymodule.requests ")? I dlaczego przekazujesz "prośby" do metody "test_my_code". Co by wyglądało na przykład z "requests.post"? – Pretzel

+1

Dekorator poprawek zastępuje moduł żądania w kodzie na próbę. Mój kod jest najprawdopodobniej niepoprawny, trzeba by łączyć każdą funkcję w żądaniach oddzielnie, aby działało. Funkcja łatana jest przekazywana jako argument do testu, więc możesz zrobić przeciwko niej dowody. Czytaj dokumenty do unittest i pozoruj więcej informacji na temat ich użycia. –

+0

Dzięki za pomoc. Dałem ci znacznik wyboru. :) – Pretzel

0

Podczas wstrzykiwania modułu wniosków może być nieco za dużo, jest to bardzo dobry p ractice mieć pewne zależności jako wstrzykiwane.

Może się zdarzyć, że pełnowymiarowa platforma jest właśnie tym, czego potrzebujesz. Do tego są doskonałe moduły, takie jak Injector.

Bardziej minimalistycznym i prostym podejściem byłoby skorzystanie z dekoratora, który wykona pracę za ciebie. Istnieje garść modułów dla tego out there.

Posiadam jeden taki moduł: Injectable, który zapewnia dekorator Python 3 @autowired, aby umożliwić łatwe i czyste wstrzykiwanie zależności.

Główne punkty tej dekorator są następujące:

  • funkcja nie musi być świadomy autowiring na wszystkich
  • zależności może być leniwy zainicjowany
  • rozmówca jest w stanie jednoznacznie przekazać instancje zależnościami razie potrzeby

Zasadniczo włączeniu kod jak ten:

def __init__(self, *, model: Model = None, service: Service = None): 
    if model is None: 
     model = Model() 

    if service is None: 
     service = Service() 

    self.model = model 
    self.service = service 
    # actual code 

w tym:

@autowired 
def __init__(self, *, model: Model, service: Service): 
    self.model = model 
    self.service = service 
    # actual code 

Nie skomplikowane rzeczy, nie ma konfiguracji, żadne przepływy egzekwowane.