factory_boy是基於thoughtbot的factory_bot的固定裝置替代品。
作為一個固定裝置替換工具,它的目標是用易於使用的複雜物件工廠來取代靜態的、難以維護的固定裝置。
factory_boy
允許您使用為當前測試定制的對象,同時僅聲明特定於測試的字段,而不是使用每種可能的極端情況組合來構建詳盡的測試設置:
class FooTests ( unittest . TestCase ):
def test_with_factory_boy ( self ):
# We need a 200€, paid order, shipping to australia, for a VIP customer
order = OrderFactory (
amount = 200 ,
status = 'PAID' ,
customer__is_vip = True ,
address__country = 'AU' ,
)
# Run the tests here
def test_without_factory_boy ( self ):
address = Address (
street = "42 fubar street" ,
zipcode = "42Z42" ,
city = "Sydney" ,
country = "AU" ,
)
customer = Customer (
first_name = "John" ,
last_name = "Doe" ,
phone = "+1234" ,
email = "[email protected]" ,
active = True ,
is_vip = True ,
address = address ,
)
# etc.
Factory_boy 旨在與各種 ORM(Django、MongoDB、SQLAlchemy)良好配合,並且可以輕鬆擴展為其他函式庫。
其主要特點包括:
PyPI:https://pypi.org/project/factory-boy/
$ pip install factory_boy
資料來源:https://github.com/FactoryBoy/factory_boy/
$ git clone git://github.com/FactoryBoy/factory_boy/
$ python setup.py install
筆記
本節提供了factory_boy 功能的快速摘要。完整文件中提供了更詳細的清單。
工廠聲明一組用於實例化 Python 物件的屬性。物件的類別必須在class Meta:
屬性的model
欄位中定義:
import factory
from . import models
class UserFactory ( factory . Factory ):
class Meta :
model = models . User
first_name = 'John'
last_name = 'Doe'
admin = False
# Another, different, factory for the same object
class AdminFactory ( factory . Factory ):
class Meta :
model = models . User
first_name = 'Admin'
last_name = 'User'
admin = True
factory_boy 與物件關係映射(ORM)工具的整合是透過特定的factory.Factory
子類別提供的:
factory.django.DjangoModelFactory
factory.mogo.MogoFactory
factory.mongoengine.MongoEngineFactory
factory.alchemy.SQLAlchemyModelFactory
更多詳細資訊可以在 ORM 部分找到。
Factory_boy 支援幾種不同的實例化策略:建置、建立和存根:
# Returns a User instance that's not saved
user = UserFactory . build ()
# Returns a saved User instance.
# UserFactory must subclass an ORM base class, such as DjangoModelFactory.
user = UserFactory . create ()
# Returns a stub object (just a bunch of attributes)
obj = UserFactory . stub ()
您可以使用 Factory 類別作為預設實例化策略的捷徑:
# Same as UserFactory.create()
user = UserFactory ()
無論使用哪種策略,都可以透過傳遞關鍵字參數來覆寫定義的屬性:
# Build a User instance and override first_name
>>> user = UserFactory.build( first_name = ' Joe ' )
>>> user.first_name
"Joe"
也可以在一次呼叫中建立一堆物件:
>>> users = UserFactory.build_batch( 10 , first_name = " Joe " )
>>> len (users)
10
>>> [user.first_name for user in users]
["Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe"]
具有隨機但現實的值的演示看起來更好;這些現實的值也可以幫助發現錯誤。為此,factory_boy 依賴優秀的 faker 函式庫:
class RandomUserFactory ( factory . Factory ):
class Meta :
model = models . User
first_name = factory . Faker ( 'first_name' )
last_name = factory . Faker ( 'last_name' )
>>> RandomUserFactory()
<User: Lucy Murray>
在測試中使用完全隨機的數據很快就會成為重現損壞的構建的問題。為此,factory_boy 提供了一個幫助程式來處理它使用的隨機種子,位於factory.random
模組中:
import factory . random
def setup_test_environment ():
factory . random . reseed_random ( 'my_awesome_project' )
# Other setup here
大多數工廠屬性可以使用定義工廠時評估的靜態值來添加,但某些屬性(例如其值是從其他元素計算得出的字段)將需要在每次生成實例時分配值。
這些“惰性”屬性可以添加如下:
class UserFactory ( factory . Factory ):
class Meta :
model = models . User
first_name = 'Joe'
last_name = 'Blow'
email = factory . LazyAttribute ( lambda a : '{}.{}@example.com' . format ( a . first_name , a . last_name ). lower ())
date_joined = factory . LazyFunction ( datetime . now )
>>> UserFactory().email
"[email protected]"
筆記
當LazyFunction
不傳送任何參數時, LazyAttribute
會使用正在建構的物件作為參數來呼叫函數。
可以使用序列產生特定格式的唯一值(例如電子郵件地址)。序列是透過使用Sequence
或裝飾器sequence
來定義的:
class UserFactory ( factory . Factory ):
class Meta :
model = models . User
email = factory . Sequence ( lambda n : 'person{}@example.com' . format ( n ))
> >> UserFactory (). email
'[email protected]'
> >> UserFactory (). email
'[email protected]'
有些物件有一個複雜的字段,它本身應該從專用的工廠定義。這是由SubFactory
幫助程式處理的:
class PostFactory ( factory . Factory ):
class Meta :
model = models . Post
author = factory . SubFactory ( UserFactory )
將使用關聯對象的策略:
# Builds and saves a User and a Post
> >> post = PostFactory ()
> >> post . id is None # Post has been 'saved'
False
> >> post . author . id is None # post.author has been saved
False
# Builds but does not save a User, and then builds but does not save a Post
> >> post = PostFactory . build ()
> >> post . id is None
True
> >> post . author . id is None
True
factory_boy
支援活躍的 Python 版本以及 PyPy3。
由於呼叫鏈較長,調試factory_boy 可能相當複雜。可透過factory
記錄器進行詳細記錄。
一個助手,factory.debug(),可以用來簡化調試:
with factory . debug ():
obj = TestModel2Factory ()
import logging
logger = logging . getLogger ( 'factory' )
logger . addHandler ( logging . StreamHandler ())
logger . setLevel ( logging . DEBUG )
這將產生類似於這些的訊息(人工縮排):
BaseFactory: Preparing tests.test_using.TestModel2Factory( extra ={})
LazyStub: Computing values for tests.test_using.TestModel2Factory( two =<OrderedDeclarationWrapper for <factory.declarations.SubFactory object at 0x1e15610>>)
SubFactory: Instantiating tests.test_using.TestModelFactory( __containers =(<LazyStub for tests.test_using.TestModel2Factory>,), one =4), create =True
BaseFactory: Preparing tests.test_using.TestModelFactory( extra ={ ' __containers ' : (<LazyStub for tests.test_using.TestModel2Factory>,), ' one ' : 4})
LazyStub: Computing values for tests.test_using.TestModelFactory( one =4)
LazyStub: Computed values, got tests.test_using.TestModelFactory( one =4)
BaseFactory: Generating tests.test_using.TestModelFactory( one =4)
LazyStub: Computed values, got tests.test_using.TestModel2Factory( two =<tests.test_using.TestModel object at 0x1e15410>)
BaseFactory: Generating tests.test_using.TestModel2Factory( two =<tests.test_using.TestModel object at 0x1e15410>)
factory_boy 是根據 MIT 許可證分發的。
應透過 GitHub Issues 打開問題;只要有可能,就應該包含拉取請求。歡迎在郵件清單中提出問題和建議。
開發依賴項可以透過以下方式安裝在 virtualenv 中:
$ pip install --editable ' .[dev] '
所有拉取請求都應通過測試套件,只需使用以下命令即可啟動測試套件:
$ make testall
為了測試覆蓋範圍,請使用:
$ make coverage
若要使用特定框架版本進行測試,您可以使用tox
目標:
# list all tox environments
$ tox --listenvs
# run tests inside a specific environment (django/mongoengine/SQLAlchemy are not installed)
$ tox -e py310
# run tests inside a specific environment (django)
$ tox -e py310-djangomain
# run tests inside a specific environment (alchemy)
$ tox -e py310-alchemy
# run tests inside a specific environment (mongoengine)
$ tox -e py310-mongo
對於有興趣將 FactoryBoy 打包到下游分發管道(例如.deb
、 .rpm
、 .ebuild
)的用戶,以下情況可能會有所幫助:
setup.cfg
中列出了套件的執行時間依賴項。 dev
和doc
附加內容涵蓋了對建置和測試庫有用的依賴項。
此外,所有開發/測試任務都是透過make(1)
驅動的。
為了運行建置步驟(目前僅適用於文件),請執行:
python setup.py egg_info
make doc
測試活動 Python 環境時,執行以下命令:
make test
筆記
您必須確保factory
模組是可導入的,因為它是從測試程式碼導入的。