Codebase list pytest-factoryboy / c837f06
New upstream version 2.0.2 Sophie Brun 4 years ago
24 changed file(s) with 2044 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 \.DS_Store
1 *.rej
2 *.py[cod]
3 /.env
4 *.orig
5
6 # C extensions
7 *.so
8
9 # Packages
10 *.egg
11 *.egg-info
12 dist
13 build
14 _build
15 eggs
16 parts
17 bin
18 var
19 sdist
20 develop-eggs
21 .installed.cfg
22 lib
23 lib64
24
25 # Installer logs
26 pip-log.txt
27
28 # Unit test / coverage reports
29 .coverage
30 .tox
31 nosetests.xml
32
33 # Translations
34 *.mo
35
36 # Mr Developer
37 .mr.developer.cfg
38 .project
39 .pydevproject
40 .cache
41 .pytest_cache
42 .ropeproject
43
44 # Sublime
45 /*.sublime-*
46
47 #PyCharm
48 /.idea
49
50 # virtualenv
51 /.Python
52 /lib
53 /include
54 /src
55 /share
56 /local
0 sudo: false
1 language: python
2 # enable Python 3.5 on travis until it will be pre-installed
3 python: 3.5
4 env:
5 matrix:
6 - TESTENV=py27-pytest2
7 - TESTENV=py34-pytest2
8 - TESTENV=py35-pytest2
9 - TESTENV=py27-pytest3
10 - TESTENV=py34-pytest3
11 - TESTENV=py35-pytest3
12 install:
13 - pip install tox
14 script: tox -e $TESTENV
15 branches:
16 except:
17 - /^\d/
18 notifications:
19 email:
20 - [email protected]
21 - [email protected]
0 Authors
1 =======
2
3 `Oleg Pidsadnyi <[email protected]>`_
4 original idea and implementation
5
6 These people have contributed to `pytest-factoryboy`, in alphabetical order:
7
8 * `Anatoly Bubenkov <[email protected]>`_
9 * `Daniel Duong <https://github.com/dduong42>`_
10 * `Daniel Hahler <https://github.com/blueyed>`_
11 * `p13773 <https://github.com/p13773>`_
12 * `Vasily Kuznetsov <https://github.com/kvas-it>`_
0 Changelog
1 =========
2
3 2.0.2
4 -----
5
6 - Fix warning `use of getfuncargvalue is deprecated, use getfixturevalue` (sliverc)
7
8
9 2.0.1
10 -----
11
12 Breaking change due to the heavy refactor of both pytest and factory_boy.
13
14 - Failing test for using a `attributes` field on the factory (blueyed)
15 - Minimal pytest version is 3.3.2 (olegpidsadnyi)
16 - Minimal factory_boy version is 2.10.0 (olegpidsadnyi)
17
18
19 1.3.2
20 -----
21
22 - use {posargs} in pytest command (blueyed)
23 - pin factory_boy<2.9 (blueyed)
24
25
26 1.3.1
27 -----
28
29 - fix LazyFixture evaluation order (olegpidsadnyi)
30
31
32 1.3.0
33 -----
34
35 - replace request._fixturedefs by request._fixture_defs (p13773)
36
37
38 1.2.2
39 -----
40
41 - fix post-generation dependencies (olegpidsadnyi)
42
43
44 1.2.1
45 -----
46
47 - automatical resolution of the post-generation dependencies (olegpidsadnyi, kvas-it)
48
49
50 1.1.6
51 -----
52
53 - fixes fixture function module name attribute (olegpidsadnyi)
54 - fixes _after_postgeneration hook invocation for deferred post-generation declarations (olegpidsadnyi)
55
56
57 1.1.5
58 -----
59
60 - support factory models to be passed as strings (bubenkoff)
61
62
63 1.1.3
64 -----
65
66 - circular dependency determination is fixed for the post-generation (olegpidsadnyi)
67
68
69 1.1.2
70 -----
71
72 - circular dependency determination is fixed for the RelatedFactory attributes (olegpidsadnyi)
73
74
75 1.1.1
76 -----
77
78 - fix installation issue when django environment is not set (bubenkoff, amakhnach)
79
80
81 1.1.0
82 -----
83
84 - fixture dependencies on deferred post-generation declarations (olegpidsadnyi)
85
86
87 1.0.3
88 -----
89
90 - post_generation extra parameters fixed (olegpidsadnyi)
91 - fixture partial specialization (olegpidsadnyi)
92 - fixes readme and example (dduong42)
93 - lazy fixtures (olegpidsadnyi)
94 - deferred post-generation evaluation (olegpidsadnyi)
95 - hooks (olegpidsadnyi)
96
97
98 1.0.2
99 -----
100
101 - refactoring of the fixture function compilation (olegpidsadnyi)
102 - related factory fix (olegpidsadnyi)
103 - post_generation fixture dependency fixed (olegpidsadnyi)
104 - model fixture registration with specific name (olegpidsadnyi)
105 - README updated (olegpidsadnyi)
106
107 1.0.1
108 -----
109
110 - use ``inflection`` package to convert camel case to underscore (bubenkoff)
111
112 1.0.0
113 -----
114
115 - initial release (olegpidsadnyi)
0 include *.rst
0 # create virtual environment
1 .env:
2 virtualenv .env
3
4 # install all needed for development
5 develop: .env
6 .env/bin/pip install -e . -r requirements-testing.txt tox
7
8 # clean the development envrironment
9 clean:
10 -rm -rf .env
0 factory_boy_ integration with the pytest_ runner
1 ================================================
2
3 .. image:: https://img.shields.io/pypi/v/pytest-factoryboy.svg
4 :target: https://pypi.python.org/pypi/pytest-factoryboy
5 .. image:: https://img.shields.io/pypi/pyversions/pytest-factoryboy.svg
6 :target: https://pypi.python.org/pypi/pytest-factoryboy
7 .. image:: https://img.shields.io/coveralls/pytest-dev/pytest-factoryboy/master.svg
8 :target: https://coveralls.io/r/pytest-dev/pytest-factoryboy
9 .. image:: https://travis-ci.org/pytest-dev/pytest-factoryboy.svg?branch=master
10 :target: https://travis-ci.org/pytest-dev/pytest-factoryboy
11 .. image:: https://readthedocs.org/projects/pytest-factoryboy/badge/?version=latest
12 :target: https://readthedocs.org/projects/pytest-factoryboy/?badge=latest
13 :alt: Documentation Status
14
15
16 pytest-factoryboy makes it easy to combine ``factory`` approach to the test setup with the ``dependency`` injection,
17 heart of the `pytest fixtures`_.
18
19 .. _factory_boy: https://factoryboy.readthedocs.io
20 .. _pytest: http://pytest.org
21 .. _pytest fixtures: https://pytest.org/latest/fixture.html
22 .. _overridden: http://pytest.org/latest/fixture.html#override-a-fixture-with-direct-test-parametrization
23
24
25 Install pytest-factoryboy
26 -------------------------
27
28 ::
29
30 pip install pytest-factoryboy
31
32
33 Concept
34 -------
35
36 Library exports a function to register factories as fixtures. Fixtures are contributed
37 to the same module where register function is called.
38
39 Factory Fixture
40 ---------------
41
42 Factory fixtures allow using factories without importing them. Name convention is lowercase-underscore
43 class name.
44
45 .. code-block:: python
46
47 import factory
48 from pytest_factoryboy import register
49
50 class AuthorFactory(factory.Factory):
51
52 class Meta:
53 model = Author
54
55
56 register(AuthorFactory)
57
58
59 def test_factory_fixture(author_factory):
60 author = author_factory(name="Charles Dickens")
61 assert author.name == "Charles Dickens"
62
63
64 Model Fixture
65 -------------
66
67 Model fixture implements an instance of a model created by the factory. Name convention is lowercase-underscore
68 class name.
69
70
71 .. code-block:: python
72
73 import factory
74 from pytest_factoryboy import register
75
76 @register
77 class AuthorFactory(Factory):
78
79 class Meta:
80 model = Author
81
82 name = "Charles Dickens"
83
84
85 def test_model_fixture(author):
86 assert author.name == "Charles Dickens"
87
88
89 Model fixtures can be registered with specific names. For example if you address instances of some collection
90 by the name like "first", "second" or of another parent as "other":
91
92
93 .. code-block:: python
94
95 register(BookFactory) # book
96 register(BookFactory, "second_book") # second_book
97
98 register(AuthorFactory) # author
99 register(AuthorFactory, "second_author") # second_author
100
101 register(BookFactory, "other_book") # other_book, book of another author
102
103 @pytest.fixture
104 def other_book__author(second_author):
105 """Make the relation of the second_book to another (second) author."""
106 return second_author
107
108
109
110 Attributes are Fixtures
111 -----------------------
112
113 There are fixtures created for factory attributes. Attribute names are prefixed with the model fixture name and
114 double underscore (similar to the convention used by factory_boy).
115
116
117 .. code-block:: python
118
119 @pytest.mark.parametrize("author__name", ["Bill Gates"])
120 def test_model_fixture(author):
121 assert author.name == "Bill Gates"
122
123 SubFactory
124 ----------
125
126 Sub-factory attribute points to the model fixture of the sub-factory.
127 Attributes of sub-factories are injected as dependencies to the model fixture and can be overridden_ via
128 the parametrization.
129
130 Related Factory
131 ---------------
132
133 Related factory attribute points to the model fixture of the related factory.
134 Attributes of related factories are injected as dependencies to the model fixture and can be overridden_ via
135 the parametrization.
136
137
138 post-generation
139 ---------------
140
141 Post-generation attribute fixture implements only the extracted value for the post generation function.
142
143
144 Integration
145 -----------
146
147 An example of factory_boy_ and pytest_ integration.
148
149 factories/__init__.py:
150
151 .. code-block:: python
152
153 import factory
154 from faker import Factory as FakerFactory
155
156 faker = FakerFactory.create()
157
158
159 class AuthorFactory(factory.django.DjangoModelFactory):
160
161 """Author factory."""
162
163 name = factory.LazyAttribute(lambda x: faker.name())
164
165 class Meta:
166 model = 'app.Author'
167
168
169 class BookFactory(factory.django.DjangoModelFactory):
170
171 """Book factory."""
172
173 title = factory.LazyAttribute(lambda x: faker.sentence(nb_words=4))
174
175 class Meta:
176 model = 'app.Book'
177
178 author = factory.SubFactory(AuthorFactory)
179
180 tests/conftest.py:
181
182 .. code-block:: python
183
184 from pytest_factoryboy import register
185
186 from factories import AuthorFactory, BookFactory
187
188 register(AuthorFactory)
189 register(BookFactory)
190
191 tests/test_models.py:
192
193 .. code-block:: python
194
195 from app.models import Book
196 from factories import BookFactory
197
198 def test_book_factory(book_factory):
199 """Factories become fixtures automatically."""
200 assert isinstance(book_factory, BookFactory)
201
202 def test_book(book):
203 """Instances become fixtures automatically."""
204 assert isinstance(book, Book)
205
206 @pytest.mark.parametrize("book__title", ["PyTest for Dummies"])
207 @pytest.mark.parametrize("author__name", ["Bill Gates"])
208 def test_parametrized(book):
209 """You can set any factory attribute as a fixture using naming convention."""
210 assert book.name == "PyTest for Dummies"
211 assert book.author.name == "Bill Gates"
212
213
214 Fixture partial specialization
215 ------------------------------
216
217 There is a possibility to pass keyword parameters in order to override factory attribute values during fixture
218 registration. This comes in handy when your test case is requesting a lot of fixture flavors. Too much for the
219 regular pytest parametrization.
220 In this case you can register fixture flavors in the local test module and specify value deviations inside ``register``
221 function calls.
222
223
224 .. code-block:: python
225
226 register(AuthorFactory, "male_author", gender="M", name="John Doe")
227 register(AuthorFactory, "female_author", gender="F")
228
229
230 @pytest.fixture
231 def female_author__name():
232 """Override female author name as a separate fixture."""
233 return "Jane Doe"
234
235
236 @pytest.mark.parametrize("male_author__age", [42]) # Override even more
237 def test_partial(male_author, female_author):
238 """Test fixture partial specialization."""
239 assert male_author.gender == "M"
240 assert male_author.name == "John Doe"
241 assert male_author.age == 42
242
243 assert female_author.gender == "F"
244 assert female_author.name == "Jane Doe"
245
246
247 Fixture attributes
248 ------------------
249
250 Sometimes it is necessary to pass an instance of another fixture as an attribute value to the factory.
251 It is possible to override the generated attribute fixture where desired values can be requested as
252 fixture dependencies. There is also a lazy wrapper for the fixture that can be used in the parametrization
253 without defining fixtures in a module.
254
255
256 LazyFixture constructor accepts either existing fixture name or callable with dependencies:
257
258 .. code-block:: python
259
260 import pytest
261 from pytest_factoryboy import register, LazyFixture
262
263
264 @pytest.mark.parametrize("book__author", [LazyFixture("another_author")])
265 def test_lazy_fixture_name(book, another_author):
266 """Test that book author is replaced with another author by fixture name."""
267 assert book.author == another_author
268
269
270 @pytest.mark.parametrize("book__author", [LazyFixture(lambda another_author: another_author)])
271 def test_lazy_fixture_callable(book, another_author):
272 """Test that book author is replaced with another author by callable."""
273 assert book.author == another_author
274
275
276 # Can also be used in the partial specialization during the registration.
277 register(BookFactory, "another_book", author=LazyFixture("another_author"))
278
279
280 Post-generation dependencies
281 ============================
282
283 Unlike factory_boy which binds related objects using an internal container to store results of lazy evaluations,
284 pytest-factoryboy relies on the PyTest request.
285
286 Circular dependencies between objects can be resolved using post-generation hooks/related factories in combination with
287 passing the SelfAttribute, but in the case of PyTest request fixture functions have to return values in order to be cached
288 in the request and to become available to other fixtures.
289
290 That's why evaluation of the post-generation declaration in pytest-factoryboy is deferred until calling
291 the test funciton.
292 This solves circular dependecy resolution for situations like:
293
294 ::
295
296 o->[ A ]-->[ B ]<--[ C ]-o
297 | |
298 o----(C depends on A)----o
299
300
301 On the other hand deferring the evaluation of post-generation declarations evaluation makes their result unavailable during the generation
302 of objects that are not in the circular dependecy, but they rely on the post-generation action.
303
304 pytest-factoryboy is trying to detect cycles and resolve post-generation dependencies automatically.
305
306
307 .. code-block:: python
308
309 from pytest_factoryboy import register
310
311
312 class Foo(object):
313
314 def __init__(self, value):
315 self.value = value
316
317
318 class Bar(object):
319
320 def __init__(self, foo):
321 self.foo = foo
322
323
324 @register
325 class FooFactory(factory.Factory):
326
327 """Foo factory."""
328
329 class Meta:
330 model = Foo
331
332 value = 0
333
334 @factory.post_generation
335 def set1(foo, create, value, **kwargs):
336 foo.value = 1
337
338
339 class BarFactory(factory.Factory):
340
341 """Bar factory."""
342
343 foo = factory.SubFactory(FooFactory)
344
345 @classmethod
346 def _create(cls, model_class, foo):
347 assert foo.value == 1 # Assert that set1 is evaluated before object generation
348 return super(BarFactory, cls)._create(model_class, foo=foo)
349
350 class Meta:
351 model = Bar
352
353
354 register(
355 BarFactory,
356 'bar',
357 )
358 """Forces 'set1' to be evaluated first."""
359
360
361 def test_depends_on_set1(bar):
362 """Test that post-generation hooks are done and the value is 2."""
363 assert depends_on_1.foo.value == 1
364
365
366 Hooks
367 -----
368
369 pytest-factoryboy exposes several `pytest hooks <http://pytest.org/latest/plugins.html#well-specified-hooks>`_
370 which might be helpful for e.g. controlling database transaction, for reporting etc:
371
372 * pytest_factoryboy_done(request) - Called after all factory based fixtures and their post-generation actions have been evaluated.
373
374
375 License
376 -------
377
378 This software is licensed under the `MIT license <http://en.wikipedia.org/wiki/MIT_License>`_.
379
380 © 2015 Oleg Pidsadnyi, Anatoly Bubenkov and others
0 # Makefile for Sphinx documentation
1 #
2
3 # You can set these variables from the command line.
4 SPHINXOPTS =
5 SPHINXBUILD = sphinx-build
6 PAPER =
7 BUILDDIR = _build
8
9 # Internal variables.
10 PAPEROPT_a4 = -D latex_paper_size=a4
11 PAPEROPT_letter = -D latex_paper_size=letter
12 ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
13 # the i18n builder cannot share the environment and doctrees with the others
14 I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
15
16 .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
17
18 help:
19 @echo "Please use \`make <target>' where <target> is one of"
20 @echo " html to make standalone HTML files"
21 @echo " dirhtml to make HTML files named index.html in directories"
22 @echo " singlehtml to make a single large HTML file"
23 @echo " pickle to make pickle files"
24 @echo " json to make JSON files"
25 @echo " htmlhelp to make HTML files and a HTML help project"
26 @echo " qthelp to make HTML files and a qthelp project"
27 @echo " devhelp to make HTML files and a Devhelp project"
28 @echo " epub to make an epub"
29 @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
30 @echo " latexpdf to make LaTeX files and run them through pdflatex"
31 @echo " text to make text files"
32 @echo " man to make manual pages"
33 @echo " texinfo to make Texinfo files"
34 @echo " info to make Texinfo files and run them through makeinfo"
35 @echo " gettext to make PO message catalogs"
36 @echo " changes to make an overview of all changed/added/deprecated items"
37 @echo " linkcheck to check all external links for integrity"
38 @echo " doctest to run all doctests embedded in the documentation (if enabled)"
39
40 clean:
41 -rm -rf $(BUILDDIR)/*
42
43 html:
44 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
45 @echo
46 @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
47
48 dirhtml:
49 $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
50 @echo
51 @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
52
53 singlehtml:
54 $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
55 @echo
56 @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
57
58 pickle:
59 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
60 @echo
61 @echo "Build finished; now you can process the pickle files."
62
63 json:
64 $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
65 @echo
66 @echo "Build finished; now you can process the JSON files."
67
68 htmlhelp:
69 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
70 @echo
71 @echo "Build finished; now you can run HTML Help Workshop with the" \
72 ".hhp project file in $(BUILDDIR)/htmlhelp."
73
74 qthelp:
75 $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
76 @echo
77 @echo "Build finished; now you can run "qcollectiongenerator" with the" \
78 ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
79 @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Pytest-BDD.qhcp"
80 @echo "To view the help file:"
81 @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Pytest-BDD.qhc"
82
83 devhelp:
84 $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
85 @echo
86 @echo "Build finished."
87 @echo "To view the help file:"
88 @echo "# mkdir -p $$HOME/.local/share/devhelp/Pytest-BDD"
89 @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Pytest-BDD"
90 @echo "# devhelp"
91
92 epub:
93 $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
94 @echo
95 @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
96
97 latex:
98 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
99 @echo
100 @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
101 @echo "Run \`make' in that directory to run these through (pdf)latex" \
102 "(use \`make latexpdf' here to do that automatically)."
103
104 latexpdf:
105 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
106 @echo "Running LaTeX files through pdflatex..."
107 $(MAKE) -C $(BUILDDIR)/latex all-pdf
108 @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
109
110 text:
111 $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
112 @echo
113 @echo "Build finished. The text files are in $(BUILDDIR)/text."
114
115 man:
116 $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
117 @echo
118 @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
119
120 texinfo:
121 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
122 @echo
123 @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
124 @echo "Run \`make' in that directory to run these through makeinfo" \
125 "(use \`make info' here to do that automatically)."
126
127 info:
128 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
129 @echo "Running Texinfo files through makeinfo..."
130 make -C $(BUILDDIR)/texinfo info
131 @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
132
133 gettext:
134 $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
135 @echo
136 @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
137
138 changes:
139 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
140 @echo
141 @echo "The overview file is in $(BUILDDIR)/changes."
142
143 linkcheck:
144 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
145 @echo
146 @echo "Link check complete; look for any errors in the above output " \
147 "or in $(BUILDDIR)/linkcheck/output.txt."
148
149 doctest:
150 $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
151 @echo "Testing of doctests in the sources finished, look at the " \
152 "results in $(BUILDDIR)/doctest/output.txt."
0 """Sphinx config."""
1 # -*- coding: utf-8 -*-
2 #
3 # pytest-factoryboy documentation build configuration file, created by
4 # sphinx-quickstart on Sun Apr 7 21:07:56 2013.
5 #
6 # This file is execfile()d with the current directory set to its containing dir.
7 #
8 # Note that not all possible configuration values are present in this
9 # autogenerated file.
10 #
11 # All configuration values have a default; values that are commented out
12 # serve to show the default.
13
14 # If extensions (or modules to document with autodoc) are in another directory,
15 # add these directories to sys.path here. If the directory is relative to the
16 # documentation root, use os.path.abspath to make it absolute, like shown here.
17
18 import sys
19 import os
20
21 sys.path.insert(0, os.path.abspath('..'))
22
23 import pytest_factoryboy
24
25 # -- General configuration -----------------------------------------------------
26
27 # If your documentation needs a minimal Sphinx version, state it here.
28 #needs_sphinx = '1.0'
29
30 # Add any Sphinx extension module names here, as strings. They can be extensions
31 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
32 extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
33
34 # Add any paths that contain templates here, relative to this directory.
35 templates_path = ['_templates']
36
37 # The suffix of source filenames.
38 source_suffix = '.rst'
39
40 # The encoding of source files.
41 #source_encoding = 'utf-8-sig'
42
43 # The master toctree document.
44 master_doc = 'index'
45
46 # General information about the project.
47 project = u'pytest-factoryboy'
48 AUTHOR = 'Oleg Pidsadnyi, Anatoly Bubenkov and others'
49 copyright = u'2015, ' + AUTHOR
50
51 # The version info for the project you're documenting, acts as replacement for
52 # |version| and |release|, also used in various other places throughout the
53 # built documents.
54 #
55 # The short X.Y version.
56 version = pytest_factoryboy.__version__
57 # The full version, including alpha/beta/rc tags.
58 release = pytest_factoryboy.__version__
59
60 # The language for content autogenerated by Sphinx. Refer to documentation
61 # for a list of supported languages.
62 #language = None
63
64 # There are two options for replacing |today|: either, you set today to some
65 # non-false value, then it is used:
66 #today = ''
67 # Else, today_fmt is used as the format for a strftime call.
68 #today_fmt = '%B %d, %Y'
69
70 # List of patterns, relative to source directory, that match files and
71 # directories to ignore when looking for source files.
72 exclude_patterns = ['_build']
73
74 # The reST default role (used for this markup: `text`) to use for all documents.
75 #default_role = None
76
77 # If true, '()' will be appended to :func: etc. cross-reference text.
78 #add_function_parentheses = True
79
80 # If true, the current module name will be prepended to all description
81 # unit titles (such as .. function::).
82 #add_module_names = True
83
84 # If true, sectionauthor and moduleauthor directives will be shown in the
85 # output. They are ignored by default.
86 #show_authors = False
87
88 # The name of the Pygments (syntax highlighting) style to use.
89 pygments_style = 'sphinx'
90
91 # A list of ignored prefixes for module index sorting.
92 #modindex_common_prefix = []
93
94
95 # -- Options for HTML output ---------------------------------------------------
96
97 # The theme to use for HTML and HTML Help pages. See the documentation for
98 # a list of builtin themes.
99 html_theme = 'default'
100
101 # Theme options are theme-specific and customize the look and feel of a theme
102 # further. For a list of options available for each theme, see the
103 # documentation.
104 #html_theme_options = {}
105
106 # Add any paths that contain custom themes here, relative to this directory.
107 #html_theme_path = []
108
109 # The name for this set of Sphinx documents. If None, it defaults to
110 # "<project> v<release> documentation".
111 #html_title = None
112
113 # A shorter title for the navigation bar. Default is the same as html_title.
114 #html_short_title = None
115
116 # The name of an image file (relative to this directory) to place at the top
117 # of the sidebar.
118 #html_logo = None
119
120 # The name of an image file (within the static path) to use as favicon of the
121 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
122 # pixels large.
123 #html_favicon = None
124
125 # Add any paths that contain custom static files (such as style sheets) here,
126 # relative to this directory. They are copied after the builtin static files,
127 # so a file named "default.css" will overwrite the builtin "default.css".
128 html_static_path = ['_static']
129
130 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
131 # using the given strftime format.
132 #html_last_updated_fmt = '%b %d, %Y'
133
134 # If true, SmartyPants will be used to convert quotes and dashes to
135 # typographically correct entities.
136 #html_use_smartypants = True
137
138 # Custom sidebar templates, maps document names to template names.
139 #html_sidebars = {}
140
141 # Additional templates that should be rendered to pages, maps page names to
142 # template names.
143 #html_additional_pages = {}
144
145 # If false, no module index is generated.
146 #html_domain_indices = True
147
148 # If false, no index is generated.
149 #html_use_index = True
150
151 # If true, the index is split into individual pages for each letter.
152 #html_split_index = False
153
154 # If true, links to the reST sources are added to the pages.
155 #html_show_sourcelink = True
156
157 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
158 #html_show_sphinx = True
159
160 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
161 #html_show_copyright = True
162
163 # If true, an OpenSearch description file will be output, and all pages will
164 # contain a <link> tag referring to it. The value of this option must be the
165 # base URL from which the finished HTML is served.
166 #html_use_opensearch = ''
167
168 # This is the file name suffix for HTML files (e.g. ".xhtml").
169 #html_file_suffix = None
170
171 # Output file base name for HTML help builder.
172 htmlhelp_basename = 'pytest-factoryboy-doc'
173
174
175 # -- Options for LaTeX output --------------------------------------------------
176
177 latex_elements = {
178 # The paper size ('letterpaper' or 'a4paper').
179 #'papersize': 'letterpaper',
180
181 # The font size ('10pt', '11pt' or '12pt').
182 #'pointsize': '10pt',
183
184 # Additional stuff for the LaTeX preamble.
185 #'preamble': '',
186 }
187
188 # Grouping the document tree into LaTeX files. List of tuples
189 # (source start file, target name, title, author, documentclass [howto/manual]).
190 latex_documents = [
191 ('index', 'pytest-factoryboy.tex', u'pytest-factoryboy Documentation', AUTHOR, 'manual'),
192 ]
193
194 # The name of an image file (relative to this directory) to place at the top of
195 # the title page.
196 #latex_logo = None
197
198 # For "manual" documents, if this is true, then toplevel headings are parts,
199 # not chapters.
200 #latex_use_parts = False
201
202 # If true, show page references after internal links.
203 #latex_show_pagerefs = False
204
205 # If true, show URL addresses after external links.
206 #latex_show_urls = False
207
208 # Documents to append as an appendix to all manuals.
209 #latex_appendices = []
210
211 # If false, no module index is generated.
212 #latex_domain_indices = True
213
214
215 # -- Options for manual page output --------------------------------------------
216
217 # One entry per manual page. List of tuples
218 # (source start file, name, description, authors, manual section).
219 man_pages = [
220 ('index', 'pytest-factoryboy', u'pytest-factoryboy Documentation',
221 [AUTHOR], 1)
222 ]
223
224 # If true, show URL addresses after external links.
225 #man_show_urls = False
226
227
228 # -- Options for Texinfo output ------------------------------------------------
229
230 # Grouping the document tree into Texinfo files. List of tuples
231 # (source start file, target name, title, author,
232 # dir menu entry, description, category)
233 texinfo_documents = [
234 ('index', 'pytest-factoryboy', u'pytest-factoryboy Documentation',
235 AUTHOR, 'pytest-factoryboy', 'factory_boy integration the pytest runner.',
236 'Miscellaneous'),
237 ]
238
239 # Documents to append as an appendix to all manuals.
240 #texinfo_appendices = []
241
242 # If false, no module index is generated.
243 #texinfo_domain_indices = True
244
245 # How to display URL addresses: 'footnote', 'no', or 'inline'.
246 #texinfo_show_urls = 'footnote'
0 Welcome to pytest-factoryboy's documentation!
1 =============================================
2
3 .. contents::
4
5 .. include:: ../README.rst
6
7 .. include:: ../AUTHORS.rst
8
9 .. include:: ../CHANGES.rst
0 """pytest-factoryboy public API."""
1 from .fixture import register, LazyFixture
2
3 __version__ = '2.0.2'
4
5
6 __all__ = [
7 register.__name__,
8 LazyFixture.__name__,
9 ]
0 """Factory boy fixture integration."""
1
2 import sys
3 import inspect
4
5 import factory
6 import factory.builder
7 import factory.declarations
8 import factory.enums
9 import inflection
10 import pytest
11
12
13 SEPARATOR = "__"
14
15
16 FIXTURE_FUNC_FORMAT = """
17 def {name}({deps}):
18 return _fixture_impl(request, **kwargs)
19 """
20
21
22 def make_fixture(name, module, func, args=None, related=None, **kwargs):
23 """Make fixture function and inject arguments.
24
25 :param name: Fixture name.
26 :param module: Python module to contribute the fixture into.
27 :param func: Fixture implementation function.
28 :param args: Argument names.
29 """
30 args = [] if args is None else list(args)
31 if "request" not in args:
32 args.insert(0, "request")
33 deps = ", ".join(args)
34 context = dict(_fixture_impl=func, kwargs=kwargs)
35 context.update(kwargs)
36 exec(FIXTURE_FUNC_FORMAT.format(name=name, deps=deps), context)
37 fixture_func = context[name]
38 fixture_func.__module__ = module.__name__
39
40 if related:
41 fixture_func._factoryboy_related = related
42
43 fixture = pytest.fixture(fixture_func)
44 setattr(module, name, fixture)
45 return fixture
46
47
48 def register(factory_class, _name=None, **kwargs):
49 r"""Register fixtures for the factory class.
50
51 :param factory_class: Factory class to register.
52 :param _name: Name of the model fixture. By default is lowercase-underscored model name.
53 :param \**kwargs: Optional keyword arguments that override factory attributes.
54 """
55 assert not factory_class._meta.abstract, "Can't register abstract factories."
56 assert factory_class._meta.model is not None, "Factory model class is not specified."
57
58 module = get_caller_module()
59 model_name = get_model_name(factory_class) if _name is None else _name
60 factory_name = get_factory_name(factory_class)
61
62 deps = get_deps(factory_class, model_name=model_name)
63 related = []
64
65 for attr, value in factory_class._meta.declarations.items():
66 args = None
67 attr_name = SEPARATOR.join((model_name, attr))
68
69 if isinstance(value, factory.declarations.PostGeneration):
70 value = kwargs.get(attr, None)
71 if isinstance(value, LazyFixture):
72 args = value.args
73
74 make_fixture(
75 name=attr_name,
76 module=module,
77 func=attr_fixture,
78 value=value,
79 args=args,
80 )
81 else:
82 value = kwargs.get(attr, value)
83
84 if isinstance(value, (factory.SubFactory, factory.RelatedFactory)):
85 subfactory_class = value.get_factory()
86 subfactory_deps = get_deps(subfactory_class, factory_class)
87
88 args = list(subfactory_deps)
89 if isinstance(value, factory.RelatedFactory):
90 related_model = get_model_name(subfactory_class)
91 args.append(related_model)
92 related.append(related_model)
93 related.append(attr_name)
94 related.extend(subfactory_deps)
95
96 if isinstance(value, factory.SubFactory):
97 args.append(inflection.underscore(subfactory_class._meta.model.__name__))
98
99 make_fixture(
100 name=attr_name,
101 module=module,
102 func=subfactory_fixture,
103 args=args,
104 factory_class=subfactory_class,
105 )
106 else:
107 if isinstance(value, LazyFixture):
108 args = value.args
109
110 make_fixture(
111 name=attr_name,
112 module=module,
113 func=attr_fixture,
114 value=value,
115 args=args,
116 )
117
118 if not hasattr(module, factory_name):
119 make_fixture(
120 name=factory_name,
121 module=module,
122 func=factory_fixture,
123 factory_class=factory_class,
124 )
125
126 make_fixture(
127 name=model_name,
128 module=module,
129 func=model_fixture,
130 args=deps,
131 factory_name=factory_name,
132 related=related,
133 )
134 return factory_class
135
136
137 def get_model_name(factory_class):
138 """Get model fixture name by factory."""
139 return (
140 inflection.underscore(factory_class._meta.model.__name__)
141 if not isinstance(factory_class._meta.model, str) else factory_class._meta.model)
142
143
144 def get_factory_name(factory_class):
145 """Get factory fixture name by factory."""
146 return inflection.underscore(factory_class.__name__)
147
148
149 def get_deps(factory_class, parent_factory_class=None, model_name=None):
150 """Get factory dependencies.
151
152 :return: List of the fixture argument names for dependency injection.
153 """
154 model_name = get_model_name(factory_class) if model_name is None else model_name
155 parent_model_name = get_model_name(parent_factory_class) if parent_factory_class is not None else None
156
157 def is_dep(value):
158 if isinstance(value, factory.RelatedFactory):
159 return False
160 if isinstance(value, factory.SubFactory) and get_model_name(value.get_factory()) == parent_model_name:
161 return False
162 if isinstance(value, factory.declarations.PostGeneration):
163 # Dependency on extracted value
164 return True
165
166 return True
167
168 return [
169 SEPARATOR.join((model_name, attr))
170 for attr, value in factory_class._meta.declarations.items()
171 if is_dep(value)
172 ]
173
174
175 def evaluate(request, value):
176 """Evaluate the declaration (lazy fixtures, etc)."""
177 return value.evaluate(request) if isinstance(value, LazyFixture) else value
178
179
180 def model_fixture(request, factory_name):
181 """Model fixture implementation."""
182 factoryboy_request = request.getfixturevalue("factoryboy_request")
183
184 # Try to evaluate as much post-generation dependencies as possible
185 factoryboy_request.evaluate(request)
186
187 factory_class = request.getfixturevalue(factory_name)
188 prefix = "".join((request.fixturename, SEPARATOR))
189
190 # Create model fixture instance
191
192 class Factory(factory_class):
193 pass
194
195 Factory._meta.base_declarations = dict(
196 (k, v) for k, v in Factory._meta.base_declarations.items()
197 if not isinstance(v, factory.declarations.PostGenerationDeclaration)
198 )
199 Factory._meta.post_declarations = factory.builder.DeclarationSet()
200
201 kwargs = {}
202 for key in factory_class._meta.pre_declarations:
203 argname = "".join((prefix, key))
204 if argname in request._fixturedef.argnames:
205 kwargs[key] = evaluate(request, request.getfixturevalue(argname))
206
207 strategy = factory.enums.CREATE_STRATEGY
208 builder = factory.builder.StepBuilder(Factory._meta, kwargs, strategy)
209 step = factory.builder.BuildStep(builder=builder, sequence=Factory._meta.next_sequence())
210
211 instance = Factory(**kwargs)
212
213 # Cache the instance value on pytest level so that the fixture can be resolved before the return
214 request._fixturedef.cached_result = (instance, 0, None)
215 request._fixture_defs[request.fixturename] = request._fixturedef
216
217 # Defer post-generation declarations
218 deferred = []
219
220 for attr in factory_class._meta.post_declarations.sorted():
221
222 decl = factory_class._meta.post_declarations.declarations[attr]
223
224 if isinstance(decl, factory.RelatedFactory):
225 deferred.append(make_deferred_related(factory_class, request.fixturename, attr))
226 else:
227 argname = "".join((prefix, attr))
228 extra = {}
229 for k, v in factory_class._meta.post_declarations.contexts[attr].items():
230 if k == '':
231 continue
232 post_attr = SEPARATOR.join((argname, k))
233
234 if post_attr in request._fixturedef.argnames:
235 extra[k] = evaluate(request, request.getfixturevalue(post_attr))
236 else:
237 extra[k] = v
238
239 postgen_context = factory.builder.PostGenerationContext(
240 value_provided=True,
241 value=evaluate(request, request.getfixturevalue(argname)),
242 extra=extra,
243 )
244 deferred.append(
245 make_deferred_postgen(step, factory_class, request.fixturename, instance, attr, decl, postgen_context)
246 )
247 factoryboy_request.defer(deferred)
248
249 # Try to evaluate as much post-generation dependencies as possible
250 factoryboy_request.evaluate(request)
251 return instance
252
253
254 def make_deferred_related(factory, fixture, attr):
255 """Make deferred function for the related factory declaration.
256
257 :param factory: Factory class.
258 :param fixture: Object fixture name e.g. "book".
259 :param attr: Declaration attribute name e.g. "publications".
260
261 :note: Deferred function name results in "book__publication".
262 """
263 name = SEPARATOR.join((fixture, attr))
264
265 def deferred(request):
266 request.getfixturevalue(name)
267
268 deferred.__name__ = name
269 deferred._factory = factory
270 deferred._fixture = fixture
271 deferred._is_related = True
272 return deferred
273
274
275 def make_deferred_postgen(step, factory_class, fixture, instance, attr, declaration, context):
276 """Make deferred function for the post-generation declaration.
277
278 :param step: factory_boy builder step.
279 :param factory_class: Factory class.
280 :param fixture: Object fixture name e.g. "author".
281 :param instance: Parent object instance.
282 :param attr: Declaration attribute name e.g. "register_user".
283 :param context: Post-generation declaration context.
284
285 :note: Deferred function name results in "author__register_user".
286 """
287 name = SEPARATOR.join((fixture, attr))
288
289 def deferred(request):
290 declaration.call(instance, step, context)
291
292 deferred.__name__ = name
293 deferred._factory = factory_class
294 deferred._fixture = fixture
295 deferred._is_related = False
296 return deferred
297
298
299 def factory_fixture(request, factory_class):
300 """Factory fixture implementation."""
301 return factory_class
302
303
304 def attr_fixture(request, value):
305 """Attribute fixture implementation."""
306 return value
307
308
309 def subfactory_fixture(request, factory_class):
310 """SubFactory/RelatedFactory fixture implementation."""
311 fixture = inflection.underscore(factory_class._meta.model.__name__)
312 return request.getfixturevalue(fixture)
313
314
315 def get_caller_module(depth=2):
316 """Get the module of the caller."""
317 frame = sys._getframe(depth)
318 module = inspect.getmodule(frame)
319 # Happens when there's no __init__.py in the folder
320 if module is None:
321 return get_caller_module(depth=depth) # pragma: no cover
322 return module
323
324
325 class LazyFixture(object):
326 """Lazy fixture."""
327
328 def __init__(self, fixture):
329 """Lazy pytest fixture wrapper.
330
331 :param fixture: Fixture name or callable with dependencies.
332 """
333 self.fixture = fixture
334 if callable(self.fixture):
335 self.args = list(inspect.getargspec(self.fixture).args)
336 else:
337 self.args = [self.fixture]
338
339 def evaluate(self, request):
340 """Evaluate the lazy fixture.
341
342 :param request: pytest request object.
343 :return: evaluated fixture.
344 """
345 if callable(self.fixture):
346 kwargs = dict((arg, request.getfixturevalue(arg)) for arg in self.args)
347 return self.fixture(**kwargs)
348 else:
349 return request.getfixturevalue(self.fixture)
0 """pytest-factoryboy pytest hooks."""
1
2
3 def pytest_factoryboy_done(request):
4 """Called after all factory based fixtures and their post-generation actions were evaluated."""
0 """pytest-factoryboy plugin."""
1
2 from collections import defaultdict
3 import pytest
4
5
6 class CycleDetected(Exception):
7 pass
8
9
10 class Request(object):
11 """PyTest FactoryBoy request."""
12
13 def __init__(self):
14 """Create pytest_factoryboy request."""
15 self.deferred = []
16 self.results = defaultdict(dict)
17 self.model_factories = {}
18 self.in_progress = set()
19
20 def defer(self, functions):
21 """Defer post-generation declaration execution until the end of the test setup.
22
23 :param functions: Functions to be deferred.
24 :note: Once already finalized all following defer calls will execute the function directly.
25 """
26 self.deferred.append(functions)
27
28 def get_deps(self, request, fixture, deps=None):
29 request = request.getfixturevalue('request')
30
31 if deps is None:
32 deps = set([fixture])
33 if fixture == 'request':
34 return deps
35
36 for fixturedef in request._fixturemanager.getfixturedefs(fixture, request._pyfuncitem.parent.nodeid) or []:
37 for argname in fixturedef.argnames:
38 if argname not in deps:
39 deps.add(argname)
40 deps.update(self.get_deps(request, argname, deps))
41 return deps
42
43 def get_current_deps(self, request):
44 deps = set()
45 while hasattr(request, '_parent_request'):
46 if request.fixturename and request.fixturename not in getattr(request, "_fixturedefs", {}):
47 deps.add(request.fixturename)
48 request = request._parent_request
49 return deps
50
51 def execute(self, request, function, deferred):
52 """"Execute deferred function and store the result."""
53 if function in self.in_progress:
54 raise CycleDetected()
55 fixture = function.__name__
56 model, attr = fixture.split("__", 1)
57 if function._is_related:
58 deps = self.get_deps(request, fixture)
59 if deps.intersection(self.get_current_deps(request)):
60 raise CycleDetected()
61 self.model_factories[model] = function._factory
62
63 self.in_progress.add(function)
64 self.results[model][attr] = function(request)
65 deferred.remove(function)
66 self.in_progress.remove(function)
67
68 def after_postgeneration(self, request):
69 """Call _after_postgeneration hooks."""
70 for model in list(self.results.keys()):
71 results = self.results.pop(model)
72 obj = request.getfixturevalue(model)
73 factory = self.model_factories[model]
74 factory._after_postgeneration(obj, create=True, results=results)
75
76 def evaluate(self, request):
77 """Finalize, run deferred post-generation actions, etc."""
78 while self.deferred:
79 try:
80 deferred = self.deferred[-1]
81 for function in list(deferred):
82 self.execute(request, function, deferred)
83 if not deferred:
84 self.deferred.remove(deferred)
85 except CycleDetected:
86 return
87
88 if not self.deferred:
89 self.after_postgeneration(request)
90
91
92 @pytest.fixture
93 def factoryboy_request():
94 """PyTest FactoryBoy request fixture."""
95 return Request()
96
97
98 @pytest.mark.tryfirst
99 def pytest_runtest_call(item):
100 """Before the test item is called."""
101 try:
102 request = item._request
103 except AttributeError:
104 # pytest-pep8 plugin passes Pep8Item here during tests.
105 return
106 factoryboy_request = request.getfixturevalue("factoryboy_request")
107 factoryboy_request.evaluate(request)
108 assert not factoryboy_request.deferred
109 request.config.hook.pytest_factoryboy_done(request=request)
110
111
112 def pytest_addhooks(pluginmanager):
113 """Register plugin hooks."""
114 from pytest_factoryboy import hooks
115 # addhooks is for older py.test and deprecated; replaced by add_hookspecs
116 add_hookspecs = getattr(pluginmanager, 'add_hookspecs', pluginmanager.addhooks)
117 add_hookspecs(hooks)
118
119
120 def pytest_generate_tests(metafunc):
121 related = []
122 for arg2fixturedef in metafunc._arg2fixturedefs.values():
123 fixturedef = arg2fixturedef[-1]
124 related.extend(getattr(fixturedef.func, "_factoryboy_related", []))
125
126 metafunc.funcargnames.extend(related)
0 mock
1 pytest-pep8
2 pytest-cov
3 pytest-cache
0 #!/usr/bin/env python
1 """pytest-factoryboy package config."""
2
3 import codecs
4 import os
5 import re
6 from setuptools import setup
7
8 dirname = os.path.dirname(__file__)
9
10 long_description = (
11 codecs.open(os.path.join(dirname, "README.rst"), encoding="utf-8").read() + "\n" +
12 codecs.open(os.path.join(dirname, "AUTHORS.rst"), encoding="utf-8").read() + "\n" +
13 codecs.open(os.path.join(dirname, "CHANGES.rst"), encoding="utf-8").read()
14 )
15
16 with codecs.open(os.path.join(dirname, 'pytest_factoryboy', '__init__.py'), encoding='utf-8') as fd:
17 VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(fd.read()).group(1)
18
19 setup(
20 name="pytest-factoryboy",
21 description="Factory Boy support for pytest.",
22 long_description=long_description,
23 author="Oleg Pidsadnyi, Anatoly Bubenkov and others",
24 license="MIT license",
25 author_email="[email protected]",
26 url="https://github.com/pytest-dev/pytest-factoryboy",
27 version=VERSION,
28 classifiers=[
29 "Development Status :: 6 - Mature",
30 "Intended Audience :: Developers",
31 "License :: OSI Approved :: MIT License",
32 "Operating System :: POSIX",
33 "Operating System :: Microsoft :: Windows",
34 "Operating System :: MacOS :: MacOS X",
35 "Topic :: Software Development :: Testing",
36 "Topic :: Software Development :: Libraries",
37 "Topic :: Utilities",
38 "Programming Language :: Python :: 2",
39 "Programming Language :: Python :: 3"
40 ] + [("Programming Language :: Python :: %s" % x) for x in "2.7 3.0 3.1 3.2 3.3 3.4 3.5".split()],
41 install_requires=[
42 "inflection",
43 "factory_boy>=2.10.0",
44 "pytest>=3.3.2",
45 ],
46 # the following makes a plugin available to py.test
47 entry_points={
48 "pytest11": [
49 "pytest-factoryboy = pytest_factoryboy.plugin",
50 ],
51 },
52 tests_require=["tox"],
53 packages=["pytest_factoryboy"],
54 include_package_data=True,
55 )
(New empty file)
0 from pytest_factoryboy import register
1 import factory
2
3 import pytest
4
5
6 class EmptyModel(object):
7 pass
8
9
10 class AttributesFactory(factory.Factory):
11 class Meta:
12 model = EmptyModel
13
14 attributes = None
15
16
17 register(AttributesFactory, "with_attributes")
18
19
20 @pytest.mark.skip(reason="Doesn't work in FactoryBoy at the moment")
21 def test_factory_with_attributes():
22 """Test that a factory can have a `attributes` field when used as a factory."""
23 AttributesFactory()
24
25
26 @pytest.mark.skip(reason="Doesn't work in FactoryBoy at the moment")
27 def test_factory_fixture_with_attributes(with_attributes):
28 """Test that a factory can have a `attributes` field when used as a fixture."""
29 pass
0 """Test circular definitions."""
1
2 import factory
3
4 from pytest_factoryboy import register
5
6
7 class Book(object):
8
9 """Book model."""
10
11 def __init__(self, name=None, price=None, author=None):
12 self.editions = []
13 self.name = name
14 self.price = price
15 self.author = author
16 self.author.books.append(self)
17
18
19 class Author(object):
20
21 """Author model."""
22
23 def __init__(self, name):
24 self.books = []
25 self.name = name
26 self.user = None
27
28
29 class AuthorFactory(factory.Factory):
30
31 class Meta:
32 model = Author
33
34 name = "Charles Dickens"
35
36 book = factory.RelatedFactory('tests.test_circular.BookFactory', 'author')
37
38
39 class BookFactory(factory.Factory):
40
41 class Meta:
42 model = Book
43
44 name = "Alice in Wonderland"
45 price = factory.LazyAttribute(lambda f: 3.99)
46 author = factory.SubFactory(AuthorFactory)
47
48
49 register(AuthorFactory)
50 register(BookFactory)
51
52
53 def test_circular(author, factoryboy_request, request):
54 assert author.books
0 """Factory fixtures tests."""
1
2 import factory
3 from factory import fuzzy
4 import pytest
5
6 from pytest_factoryboy import register, LazyFixture
7
8
9 class User(object):
10 """User account."""
11
12 def __init__(self, username, password, is_active):
13 self.username = username
14 self.password = password
15 self.is_active = is_active
16
17
18 class Book(object):
19 """Book model."""
20
21 def __init__(self, name=None, price=None, author=None):
22 self.editions = []
23 self.name = name
24 self.price = price
25 self.author = author
26
27
28 class Author(object):
29 """Author model."""
30
31 def __init__(self, name):
32 self.name = name
33 self.user = None
34
35
36 class Edition(object):
37 """Book edition."""
38
39 def __init__(self, book, year):
40 self.book = book
41 self.year = year
42 book.editions.append(self)
43
44
45 class UserFactory(factory.Factory):
46 """User factory."""
47
48 class Meta:
49 model = User
50
51 password = fuzzy.FuzzyText(length=7)
52
53
54 @register
55 class AuthorFactory(factory.Factory):
56 """Author factory."""
57
58 class Meta:
59 model = Author
60
61 name = "Charles Dickens"
62
63 register_user__is_active = True # Make sure fixture is generated
64 register_user__password = "qwerty" # Make sure fixture is generated
65
66 @factory.post_generation
67 def register_user(author, create, username, **kwargs):
68 """Register author as a user in the system."""
69 if username is not None:
70 author.user = UserFactory(username=username, **kwargs)
71
72
73 class BookFactory(factory.Factory):
74 """Test factory with all the features."""
75
76 class Meta:
77 model = Book
78
79 name = "Alice in Wonderland"
80 price = factory.LazyAttribute(lambda f: 3.99)
81 author = factory.SubFactory(AuthorFactory)
82 book_edition = factory.RelatedFactory("tests.test_factory_fixtures.EditionFactory", "book")
83
84
85 class EditionFactory(factory.Factory):
86 """Book edition factory."""
87
88 class Meta:
89 model = Edition
90
91 book = factory.SubFactory(BookFactory)
92 year = 1999
93
94
95 register(BookFactory)
96 register(EditionFactory)
97
98
99 def test_factory(book_factory):
100 """Test model factory fixture."""
101 assert book_factory == BookFactory
102
103
104 def test_model(book):
105 """Test model fixture."""
106 assert book.name == "Alice in Wonderland"
107 assert book.price == 3.99
108 assert book.author.name == "Charles Dickens"
109 assert book.author.user is None
110 assert book.editions[0].year == 1999
111 assert book.editions[0].book == book
112
113
114 def test_attr(book__name, book__price, author__name, edition__year):
115 """Test attribute fixtures.
116
117 :note: Most of the attributes are lazy definitions. Use attribute fixtures in
118 order to override the initial values.
119 """
120 assert book__name == "Alice in Wonderland"
121 assert book__price == BookFactory.price
122 assert author__name == "Charles Dickens"
123 assert edition__year == 1999
124
125
126 @pytest.mark.parametrize("book__name", ["PyTest for Dummies"])
127 @pytest.mark.parametrize("book__price", [1.0])
128 @pytest.mark.parametrize("author__name", ["Bill Gates"])
129 @pytest.mark.parametrize("edition__year", [2000])
130 def test_parametrized(book):
131 """Test model factory fixture."""
132 assert book.name == "PyTest for Dummies"
133 assert book.price == 1.0
134 assert book.author.name == "Bill Gates"
135 assert len(book.editions) == 1
136 assert book.editions[0].year == 2000
137
138
139 @pytest.mark.parametrize("author__register_user", ["admin"])
140 def test_post_generation(author):
141 """Test post generation declaration."""
142 assert author.user.username == "admin"
143 assert author.user.is_active is True
144
145
146 register(AuthorFactory, "second_author")
147
148
149 @pytest.mark.parametrize("second_author__name", ["Mr. Hyde"])
150 def test_second_author(author, second_author):
151 """Test factory registration with specific name."""
152 assert author != second_author
153 assert second_author.name == "Mr. Hyde"
154
155
156 register(AuthorFactory, "partial_author", name="John Doe", register_user=LazyFixture(lambda: "[email protected]"))
157
158
159 def test_partial(partial_author):
160 """Test fixture partial specialization."""
161 assert partial_author.name == "John Doe"
162 assert partial_author.user.username == "[email protected]"
163
164
165 register(AuthorFactory, "another_author", name=LazyFixture(lambda: "Another Author"))
166
167
168 @pytest.mark.parametrize("book__author", [LazyFixture("another_author")])
169 def test_lazy_fixture_name(book, another_author):
170 """Test that book author is replaced with another author by fixture name."""
171 assert book.author == another_author
172 assert book.author.name == "Another Author"
173
174
175 @pytest.mark.parametrize("book__author", [LazyFixture(lambda another_author: another_author)])
176 def test_lazy_fixture_callable(book, another_author):
177 """Test that book author is replaced with another author by callable."""
178 assert book.author == another_author
179 assert book.author.name == "Another Author"
180
181
182 @pytest.mark.parametrize(
183 ("author__register_user", "author__register_user__password"),
184 [
185 (LazyFixture(lambda: "lazyfixture"), LazyFixture(lambda: "asdasd")),
186 ]
187 )
188 def test_lazy_fixture_post_generation(author):
189 """Test that post-generation values are replaced with lazy fixtures."""
190 # assert author.user.username == "lazyfixture"
191 assert author.user.password == "asdasd"
0 """Test LazyFixture related features."""
1
2 import factory
3 import pytest
4
5 from pytest_factoryboy import register, LazyFixture
6
7
8 class User(object):
9 """User account."""
10
11 def __init__(self, username, password, is_active):
12 self.username = username
13 self.password = password
14 self.is_active = is_active
15
16
17 class UserFactory(factory.Factory):
18 """User factory."""
19
20 class Meta:
21 model = User
22
23 username = factory.faker.Faker("user_name")
24 password = factory.faker.Faker("password")
25 is_active = factory.LazyAttribute(lambda f: f.password == "ok")
26
27
28 register(UserFactory)
29
30
31 register(
32 UserFactory,
33 "partial_user",
34 password=LazyFixture("ok_password"),
35 )
36
37
38 @pytest.fixture
39 def ok_password():
40 return "ok"
41
42
43 @pytest.mark.parametrize("user__password", [LazyFixture("ok_password")])
44 def test_lazy_attribute(user):
45 """Test LazyFixture value is extracted before the LazyAttribute is called."""
46 assert user.is_active
47
48
49 def test_lazy_attribute_partial(partial_user):
50 """Test LazyFixture value is extracted before the LazyAttribute is called. Partial."""
51 assert partial_user.is_active
0 """Test post-generation dependecies."""
1
2 import factory
3 import pytest
4
5 from pytest_factoryboy import register
6
7
8 class Foo(object):
9
10 def __init__(self, value, expected):
11 self.value = value
12 self.expected = expected
13
14
15 class Bar(object):
16
17 def __init__(self, foo):
18 self.foo = foo
19
20
21 @register
22 class FooFactory(factory.Factory):
23
24 """Foo factory."""
25
26 class Meta:
27 model = Foo
28
29 value = 0
30 expected = 0
31 """Value that is expected at the constructor."""
32
33 @factory.post_generation
34 def set1(foo, create, value, **kwargs):
35 foo.value = 1
36
37 @classmethod
38 def _after_postgeneration(cls, obj, create, results=None):
39 obj._postgeneration_results = results
40 obj._create = create
41
42
43 class BarFactory(factory.Factory):
44
45 """Bar factory."""
46
47 foo = factory.SubFactory(FooFactory)
48
49 @classmethod
50 def _create(cls, model_class, foo):
51 assert foo.value == foo.expected
52 bar = super(BarFactory, cls)._create(model_class, foo=foo)
53 foo.bar = bar
54 return bar
55
56 class Meta:
57 model = Bar
58
59
60 def test_postgen_invoked(foo):
61 """Test that post-generation hooks are done and the value is 2."""
62 assert foo.value == 1
63
64
65 register(BarFactory)
66
67
68 @pytest.mark.parametrize('foo__value', [3])
69 @pytest.mark.parametrize('foo__expected', [1])
70 def test_depends_on(bar):
71 """Test that post-generation hooks are done and the value is 1."""
72 assert bar.foo.value == 1
73
74
75 def test_getfixturevalue(request, factoryboy_request):
76 """Test post-generation declarations via the getfixturevalue."""
77 foo = request.getfixturevalue('foo')
78 assert not factoryboy_request.deferred
79 assert foo.value == 1
80
81
82 def test_after_postgeneration(foo):
83 """Test _after_postgeneration is called."""
84 assert foo._postgeneration_results == {'set1': None}
85 assert foo._create is True
86
87
88 class Ordered(object):
89 value = None
90
91
92 @register
93 class OrderedFactory(factory.Factory):
94
95 class Meta:
96 model = Ordered
97
98 @factory.post_generation
99 def zzz(obj, create, val, **kwargs):
100 obj.value = "zzz"
101
102 @factory.post_generation
103 def aaa(obj, create, val, **kwargs):
104 obj.value = "aaa"
105
106
107 def test_ordered(ordered):
108 """Test post generation are ordered by creation counter."""
109 assert ordered.value == "aaa"
0 """Test factory registration validation."""
1
2 import factory
3 import pytest
4 from pytest_factoryboy import register
5
6
7 class WithoutModelFactory(factory.Factory):
8 """A factory without model."""
9
10
11 class AbstractFactory(factory.Factory):
12 """Abstract factory."""
13
14 class Meta:
15 abstract = True
16 model = dict
17
18
19 def test_without_model():
20 """Test that factory without model can't be registered."""
21 with pytest.raises(AssertionError):
22 register(WithoutModelFactory)
23
24
25 def test_abstract():
26 with pytest.raises(AssertionError):
27 register(AbstractFactory)
0 [tox]
1 distshare = {homedir}/.tox/distshare
2 envlist = py{27,34,35}-pytest{2,3}
3
4
5 [testenv]
6 commands = py.test --junitxml={envlogdir}/junit-{envname}.xml {posargs:tests}
7 deps = -r{toxinidir}/requirements-testing.txt
8 pytest2: pytest<3.0
9 pytest3: pytest>3.0
10
11 [pytest]
12 addopts = -vv -l --pep8
13 pep8maxlinelength = 120