Итак, представьте, что у меня есть простая библиотека, для которой я пытаюсь написать модульные тесты. Эта библиотека взаимодействует с базой данных, а затем использует эти данные для вызова SOAP API. У меня есть три модуля и тестовый файл для каждого модуля.
структура каталога:
./mypkg
../__init__.py
../main.py
../db.py
../api.py
./tests
../test_main
../test_db
../test_api
Код:
#db.py
import mysqlclient
class Db(object):
def __init__(self):
self._client = mysqlclient.Client()
@property
def data(self):
return self._client.some_query()
#api.py
import soapclient
class Api(object):
def __init__(self):
self._client = soapclient.Client()
@property
def call(self):
return self._client.some_external_call()
#main.py
from db import Db
from api import Api
class MyLib(object):
def __init__(self):
self.db = Db()
self.api = Api()
def caller(self):
return self.api.call(self.db.data)
Юнит-тесты:
#test_db.py
import mock
from mypkg.db import Db
@mock.patch('mypkg.db.mysqlclient')
def test_db(mysqlclient_mock):
mysqlclient_mock.Client.return_value.some_query = {'data':'data'}
db = Db()
assert db.data == {'data':'data'}
#test_api.py
import mock
from mypkg.api import Api
@mock.patch('mypkg.db.soapclient')
def test_db(soap_mock):
soap_mock.Client.return_value.some_external_call = 'foo'
api = Api()
assert api.call == 'foo'
В приведенном выше примере mypkg.main.MyLib
вызывает mypkg.db.Db()
(использует сторонний mysqlclient
), а затем mypkg.api.Api()
(использует сторонний soapclient
).
Я использую mock.patch
для исправления сторонних библиотек, чтобы имитировать мои вызовы db и API в test_db
и test_api
по отдельности.
Теперь мой вопрос: рекомендуется ли снова исправлять эти внешние вызовы в test_main
ИЛИ просто исправлять db.Db
и api.Api
? (этот пример довольно прост, но в больших библиотеках код становится громоздким при повторном исправлении внешних вызовов или даже при использовании тестовых вспомогательных функций, которые исправляют внутренние библиотеки).
Вариант 1: снова пропатчить внешние библиотеки в main
#test_main.py
import mock
from mypkg.main import MyLib
@mock.patch('mypkg.db.mysqlclient')
@mock.patch('mypkg.api.soapclient')
def test_main(soap_mock, mysqlcient_mock):
ml = MyLib()
soap_mock.Client.return_value.some_external_call = 'foo'
assert ml.caller() == 'foo'
Вариант 2: пропатчить внутренние библиотеки
#test_main.py
import mock
from mypkg.main import MyLib
@mock.patch('mypkg.db.Db')
@mock.patch('mypkg.api.Api')
def test_main(api_mock, db_mock):
ml = MyLib()
api_mock.return_value = 'foo'
assert ml.caller() == 'foo'