Wspomaganie testów za pomocą ludibrio

W testach zakładamy jakieś testowe dane wejściowe dla których określamy oczekiwane poprawne dane wyjściowe. Problem pojawia się gdy nie możemy ustawić wszystkich danych wejściowych - np. gdy kod pobiera dane z zewnętrznego API, albo generuje losowe dane i je dalej przetwarza, formatuje. Pomocna do testowania takich "trudnych" przypadków może być biblioteka ludibrio.

Instalacja jest standardowa - pip install ludibrio. Sama biblioteka pozwala na tworzenie moków, czy "stubowania" różnych klas, funkcji - tworzenie obiektów, które udają oryginały, lecz zachowują się w zaprogramowany przez nas sposób. Np. random.randint zwraca losową liczbę z podanego przedziału. Stub zdefiniowany za pomocą ludibrio może sprawić że random.randint zwracać będzie określoną przez nas wartość umożliwiając przetestowanie kodu.

Oto przykładowa funkcja:
import random


def make_magic_number():
  number = random.randint(1, 100000)
  # some magic we would need to test
  return '%010d' % number
Losuje ona liczbę, po czym zwraca ją odpowiednio sformatowaną. Zakładając że to "formatowanie" byłoby bardziej złożone wskazane byłoby dokładne przetestowanie takiego kodu. Niestety nad "random.randint" nie mamy kontroli, chyba że zastosujemy właśnie ludibrio:
from ludibrio import Stub
import random


def make_magic_number():
  number = random.randint(1, 100000)
  # some magic we would need to test
  return '%010d' % number

with Stub() as random.randint:
  random.randint(1, 100000) >> 5

print make_magic_number()

Stub spowoduje że wywołanie random.randint(1, 100000) (i tylko takie) zwróci podaną przez nas wartość 5. Dzięki temu możemy w teście przewidzieć poprawną wartość jaką funkcja powinna zwrócić.

Podobnie ma się sprawa z kodem pobierającym jakieś dane poprzez sieć. Pobierając np. listę zdjęć z Flickra, listę klipów wideo z YouTube itp. nie jesteśmy w stanie przewidzieć jakie dane otrzymamy - pojawią się nowe zdjęcia i klipy. Ludibrio może nam pomóc - może spowodować że samo pobieranie zwróci nasze dane. Dzięki temu możemy przetestować kod parsujący i przetwarzający pobrane dane. Oto przykład:

# -*- coding: utf-8 -*-
import json
from ludibrio import Stub
import urllib2

url = 'http://www.flickr.com/services/rest/?method=flickr.photos.search&format=json&text=python&api_key=API_KEY'

def get_python_images(url):
  handle = urllib2.urlopen(url)
  data = handle.read()
  # a bit of black magic that could use some tests
  data = data.replace('jsonFlickrApi(', '')[:-1]
  parsed_data = json.loads(data)
  return parsed_data

fake_response = 'jsonFlickrApi({"photos":{"page":1, "pages":666, "perpage":66, "total":"6666", "photo":[{"id":"123", "owner":"1111111@N06", "secret":"abcabc", "server":"1", "farm":1, "title":"Test Python", "ispublic":1, "isfriend":0, "isfamily":0}]}, "stat":"ok"})'

with Stub() as urllib2.urlopen:
  urllib2.urlopen(url).read() >> fake_response

print get_python_images(url)
W powyższym przykładzie stubujemy urllib2.urlopen, które zwróci nasze testowe dane. Dzięki temu możemy dokładnie przetestować kod obrabiający te dane.
RkBlog

Django, 2 September 2012

Comment article
Comment article RkBlog main page Search RSS Contact