aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
l---------ChangeLog1
-rw-r--r--README12
-rw-r--r--docs/changelog.rst247
-rw-r--r--docs/conf.py19
l---------[-rw-r--r--]docs/index.rst72
-rw-r--r--factory/__init__.py1
-rw-r--r--factory/containers.py4
-rw-r--r--factory/declarations.py29
-rw-r--r--factory/utils.py32
-rwxr-xr-xsetup.py18
-rw-r--r--tests/cyclic/__init__.py0
-rw-r--r--tests/cyclic/bar.py37
-rw-r--r--tests/cyclic/foo.py38
-rw-r--r--tests/test_base.py46
-rw-r--r--tests/test_containers.py11
-rw-r--r--tests/test_using.py222
-rw-r--r--tests/test_utils.py34
17 files changed, 665 insertions, 158 deletions
diff --git a/ChangeLog b/ChangeLog
new file mode 120000
index 0000000..6f0024c
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1 @@
+docs/changelog.rst \ No newline at end of file
diff --git a/README b/README
index 4ecc415..cc26087 100644
--- a/README
+++ b/README
@@ -53,7 +53,9 @@ Usage
Defining factories
""""""""""""""""""
-Factories declare a set of attributes used to instantiate an object. The class of the object must be defined in the FACTORY_FOR attribute::
+Factories declare a set of attributes used to instantiate an object. The class of the object must be defined in the FACTORY_FOR attribute:
+
+.. code-block:: python
import factory
from . import models
@@ -77,7 +79,9 @@ Factories declare a set of attributes used to instantiate an object. The class o
Using factories
"""""""""""""""
-factory_boy supports several different build strategies: build, create, attributes and stub::
+factory_boy supports several different build strategies: build, create, attributes and stub:
+
+.. code-block:: python
# Returns a User instance that's not saved
user = UserFactory.build()
@@ -89,7 +93,9 @@ factory_boy supports several different build strategies: build, create, attribut
attributes = UserFactory.attributes()
-You can use the Factory class as a shortcut for the default build strategy::
+You can use the Factory class as a shortcut for the default build strategy:
+
+.. code-block:: python
# Same as UserFactory.create()
user = UserFactory()
diff --git a/docs/changelog.rst b/docs/changelog.rst
new file mode 100644
index 0000000..173c40f
--- /dev/null
+++ b/docs/changelog.rst
@@ -0,0 +1,247 @@
+ChangeLog
+=========
+
+2.0.2 (2013-04-16)
+------------------
+
+*New:*
+
+ - When :attr:`~factory.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE` is
+ empty, use ``Model.objects.create()`` instead of ``Model.objects.get_or_create``.
+
+
+2.0.1 (2013-04-16)
+------------------
+
+*New:*
+
+ - Don't push ``defaults`` to ``get_or_create`` when
+ :attr:`~factory.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE` is not set.
+
+
+2.0.0 (2013-04-15)
+------------------
+
+*New:*
+
+ - Allow overriding the base factory class for :func:`~factory.make_factory` and friends.
+ - Add support for Python3 (Thanks to `kmike <https://github.com/kmike>`_ and `nkryptic <https://github.com/nkryptic>`_)
+ - The default :attr:`~factory.Sequence.type` for :class:`~factory.Sequence` is now :obj:`int`
+ - Fields listed in :attr:`~factory.Factory.FACTORY_HIDDEN_ARGS` won't be passed to
+ the associated class' constructor
+ - Add support for ``get_or_create`` in :class:`~factory.DjangoModelFactory`,
+ through :attr:`~factory.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE`.
+ - Add support for :mod:`~factory.fuzzy` attribute definitions.
+ - The :class:`Sequence` counter can be overridden when calling a generating function
+ - Add :class:`~factory.Dict` and :class:`~factory.List` declarations (Closes #18).
+
+*Removed:*
+
+ - Remove associated class discovery
+ - Remove :class:`~factory.InfiniteIterator` and :func:`~factory.infinite_iterator`
+ - Remove :class:`~factory.CircularSubFactory`
+ - Remove ``extract_prefix`` kwarg to post-generation hooks.
+ - Stop defaulting to Django's ``Foo.objects.create()`` when "creating" instances
+ - Remove STRATEGY_*
+ - Remove :meth:`~factory.Factory.set_building_function` / :meth:`~factory.Factory.set_creation_function`
+
+
+1.3.0 (2013-03-11)
+------------------
+
+.. warning:: This version deprecates many magic or unexplicit features that will be
+ removed in v2.0.0.
+
+ Please read the :ref:`changelog-1-3-0-upgrading` section, then run your
+ tests with ``python -W default`` to see all remaining warnings.
+
+New
+"""
+
+- **Global:**
+ - Rewrite the whole documentation
+ - Provide a dedicated :class:`~factory.MogoFactory` subclass of :class:`~factory.Factory`
+
+- **The Factory class:**
+ - Better creation/building customization hooks at :meth:`factory.Factory._build` and :meth:`factory.Factory.create`
+ - Add support for passing non-kwarg parameters to a :class:`~factory.Factory`
+ wrapped class through :attr:`~factory.Factory.FACTORY_ARG_PARAMETERS`.
+ - Keep the :attr:`~factory.Factory.FACTORY_FOR` attribute in :class:`~factory.Factory` classes
+
+- **Declarations:**
+ - Allow :class:`~factory.SubFactory` to solve circular dependencies between factories
+ - Enhance :class:`~factory.SelfAttribute` to handle "container" attribute fetching
+ - Add a :attr:`~factory.Iterator.getter` to :class:`~factory.Iterator`
+ declarations
+ - A :class:`~factory.Iterator` may be prevented from cycling by setting
+ its :attr:`~factory.Iterator.cycle` argument to ``False``
+ - Allow overriding default arguments in a :class:`~factory.PostGenerationMethodCall`
+ when generating an instance of the factory
+ - An object created by a :class:`~factory.DjangoModelFactory` will be saved
+ again after :class:`~factory.PostGeneration` hooks execution
+
+
+Pending deprecation
+"""""""""""""""""""
+
+The following features have been deprecated and will be removed in an upcoming release.
+
+- **Declarations:**
+ - :class:`~factory.InfiniteIterator` is deprecated in favor of :class:`~factory.Iterator`
+ - :class:`~factory.CircularSubFactory` is deprecated in favor of :class:`~factory.SubFactory`
+ - The ``extract_prefix`` argument to :meth:`~factory.post_generation` is now deprecated
+
+- **Factory:**
+ - Usage of :meth:`~factory.Factory.set_creation_function` and :meth:`~factory.Factory.set_building_function`
+ are now deprecated
+ - Implicit associated class discovery is no longer supported, you must set the :attr:`~factory.Factory.FACTORY_FOR`
+ attribute on all :class:`~factory.Factory` subclasses
+
+
+.. _changelog-1-3-0-upgrading:
+
+Upgrading
+"""""""""
+
+This version deprecates a few magic or undocumented features.
+All warnings will turn into errors starting from v2.0.0.
+
+In order to upgrade client code, apply the following rules:
+
+- Add a ``FACTORY_FOR`` attribute pointing to the target class to each
+ :class:`~factory.Factory`, instead of relying on automagic associated class
+ discovery
+- When using factory_boy for Django models, have each factory inherit from
+ :class:`~factory.DjangoModelFactory`
+- Replace ``factory.CircularSubFactory('some.module', 'Symbol')`` with
+ ``factory.SubFactory('some.module.Symbol')``
+- Replace ``factory.InfiniteIterator(iterable)`` with ``factory.Iterator(iterable)``
+- Replace ``@factory.post_generation()`` with ``@factory.post_generation``
+- Replace ``factory.set_building_function(SomeFactory, building_function)`` with
+ an override of the :meth:`~factory.Factory._build` method of ``SomeFactory``
+- Replace ``factory.set_creation_function(SomeFactory, creation_function)`` with
+ an override of the :meth:`~factory.Factory._create` method of ``SomeFactory``
+
+
+
+1.2.0 (2012-09-08)
+------------------
+
+*New:*
+
+ - Add :class:`~factory.CircularSubFactory` to solve circular dependencies between factories
+
+1.1.5 (2012-07-09)
+------------------
+
+*Bugfix:*
+
+ - Fix :class:`~factory.PostGenerationDeclaration` and derived classes.
+
+1.1.4 (2012-06-19)
+------------------
+
+*New:*
+
+ - Add :meth:`~factory.use_strategy` decorator to override a
+ :class:`~factory.Factory`'s default strategy
+ - Improve test running (tox, python2.6/2.7)
+ - Introduce :class:`~factory.PostGeneration` and
+ :class:`~factory.RelatedFactory`
+
+1.1.3 (2012-03-09)
+------------------
+
+*Bugfix:*
+
+ - Fix packaging rules
+
+1.1.2 (2012-02-25)
+------------------
+
+*New:*
+
+ - Add :class:`~factory.Iterator` and :class:`~factory.InfiniteIterator` for :class:`~factory.Factory` attribute declarations.
+ - Provide :func:`~factory.Factory.generate` and :func:`~factory.Factory.simple_generate`, that allow specifying the instantiation strategy directly.
+ Also provides :func:`~factory.Factory.generate_batch` and :func:`~factory.Factory.simple_generate_batch`.
+
+1.1.1 (2012-02-24)
+------------------
+
+*New:*
+
+ - Add :func:`~factory.Factory.build_batch`, :func:`~factory.Factory.create_batch` and :func:`~factory.Factory.stub_batch`, to instantiate factories in batch
+
+1.1.0 (2012-02-24)
+------------------
+
+*New:*
+
+ - Improve the :class:`~factory.SelfAttribute` syntax to fetch sub-attributes using the ``foo.bar`` syntax;
+ - Add :class:`~factory.ContainerAttribute` to fetch attributes from the container of a :class:`~factory.SubFactory`.
+ - Provide the :func:`~factory.make_factory` helper: ``MyClassFactory = make_factory(MyClass, x=3, y=4)``
+ - Add :func:`~factory.build`, :func:`~factory.create`, :func:`~factory.stub` helpers
+
+*Bugfix:*
+
+ - Allow classmethod/staticmethod on factories
+
+*Deprecation:*
+
+ - Auto-discovery of :attr:`~factory.Factory.FACTORY_FOR` based on class name is now deprecated
+
+1.0.4 (2011-12-21)
+------------------
+
+*New:*
+
+ - Improve the algorithm for populating a :class:`~factory.Factory` attributes dict
+ - Add ``python setup.py test`` command to run the test suite
+ - Allow custom build functions
+ - Introduce :data:`~factory.MOGO_BUILD` build function
+ - Add support for inheriting from multiple :class:`~factory.Factory`
+ - Base :class:`~factory.Factory` classes can now be declared :attr:`abstract <factory.Factory.ABSTRACT_FACTORY>`.
+ - Provide :class:`~factory.DjangoModelFactory`, whose :class:`~factory.Sequence` counter starts at the next free database id
+ - Introduce :class:`~factory.SelfAttribute`, a shortcut for ``factory.LazyAttribute(lambda o: o.foo.bar.baz``.
+
+*Bugfix:*
+
+ - Handle nested :class:`~factory.SubFactory`
+ - Share sequence counter between parent and subclasses
+ - Fix :class:`~factory.SubFactory` / :class:`~factory.Sequence` interferences
+
+1.0.2 (2011-05-16)
+------------------
+
+*New:*
+
+ - Introduce :class:`~factory.SubFactory`
+
+1.0.1 (2011-05-13)
+------------------
+
+*New:*
+
+ - Allow :class:`~factory.Factory` inheritance
+ - Improve handling of custom build/create functions
+
+*Bugfix:*
+
+ - Fix concurrency between :class:`~factory.LazyAttribute` and :class:`~factory.Sequence`
+
+1.0.0 (2010-08-22)
+------------------
+
+*New:*
+
+ - First version of factory_boy
+
+
+Credits
+-------
+
+* Initial version by Mark Sandstrom (2010)
+* Developed by Raphaël Barrois since 2011
+
+
+.. vim:et:ts=4:sw=4:tw=119:ft=rst:
diff --git a/docs/conf.py b/docs/conf.py
index 47630d3..0ccaf29 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -48,10 +48,23 @@ copyright = u'2011-2013, Raphaël Barrois, Mark Sandstrom'
# |version| and |release|, also used in various other places throughout the
# built documents.
#
-# The short X.Y version.
-version = '1.1'
+root = os.path.abspath(os.path.dirname(__file__))
+def get_version(*module_dir_components):
+ import re
+ version_re = re.compile(r"^__version__ = ['\"](.*)['\"]$")
+ module_root = os.path.join(root, os.pardir, *module_dir_components)
+ module_init = os.path.join(module_root, '__init__.py')
+ with open(module_init, 'r') as f:
+ for line in f:
+ match = version_re.match(line[:-1])
+ if match:
+ return match.groups()[0]
+ return '0.1.0'
+
# The full version, including alpha/beta/rc tags.
-release = '1.1.2'
+release = get_version('factory')
+# The short X.Y version.
+version = '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/index.rst b/docs/index.rst
index f0b5dcc..89a0106 100644..120000
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,71 +1 @@
-Welcome to Factory Boy's documentation!
-=======================================
-
-factory_boy provides easy replacement for fixtures, based on thoughtbot's `factory_girl <http://github.com/thoughtbot/factory_girl>`_.
-
-It allows for an easy definition of factories, various build factories, factory inheritance, ...
-
-
-Example
--------
-
-Defining a factory
-""""""""""""""""""
-
-Simply subclass the :py:class:`~factory.Factory` class, adding various class attributes which will be used as defaults::
-
- import factory
-
- class MyUserFactory(factory.Factory):
- FACTORY_FOR = MyUser # Define the related object
-
- # A simple attribute
- first_name = 'Foo'
-
- # A 'sequential' attribute: each instance of the factory will have a different 'n'
- last_name = factory.Sequence(lambda n: 'Bar' + n)
-
- # A 'lazy' attribute: computed from the values of other attributes
- email = factory.LazyAttribute(lambda o: '%s.%s@example.org' % (o.first_name.lower(), o.last_name.lower()))
-
-Using a factory
-"""""""""""""""
-
-Once defined, a factory can be instantiated through different methods::
-
- # Calls MyUser(first_name='Foo', last_name='Bar0', email='foo.bar0@example.org')
- >>> user = MyUserFactory.build()
-
- # Calls MyUser.objects.create(first_name='Foo', last_name='Bar1', email='foo.bar1@example.org')
- >>> user = MyUserFactory.create()
-
- # Values can be overridden
- >>> user = MyUserFactory.build(first_name='Baz')
- >>> user.email
- 'baz.bar2@example.org'
-
- # Additional values can be specified
- >>> user = MyUserFactory.build(some_other_var=42)
- >>> user.some_other_var
- 42
-
-
-
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
- examples
- subfactory
- post_generation
- internals
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
+../README.rst \ No newline at end of file
diff --git a/factory/__init__.py b/factory/__init__.py
index 88865ba..e1138fa 100644
--- a/factory/__init__.py
+++ b/factory/__init__.py
@@ -50,6 +50,7 @@ from .declarations import (
Dict,
List,
PostGeneration,
+ PostGenerationMethodCall,
RelatedFactory,
)
diff --git a/factory/containers.py b/factory/containers.py
index c8be431..ee2ad82 100644
--- a/factory/containers.py
+++ b/factory/containers.py
@@ -124,7 +124,7 @@ class DeclarationDict(dict):
return False
elif isinstance(value, declarations.OrderedDeclaration):
return True
- return (not name.startswith("_"))
+ return (not name.startswith("_") and not name.startswith("FACTORY_"))
def update_with_public(self, d):
"""Updates the DeclarationDict from a class definition dict.
@@ -167,7 +167,7 @@ class PostGenerationDeclarationDict(DeclarationDict):
class LazyValue(object):
"""Some kind of "lazy evaluating" object."""
- def evaluate(self, obj, containers=()):
+ def evaluate(self, obj, containers=()): # pragma: no cover
"""Compute the value, using the given object."""
raise NotImplementedError("This is an abstract method.")
diff --git a/factory/declarations.py b/factory/declarations.py
index 969d780..974b4ac 100644
--- a/factory/declarations.py
+++ b/factory/declarations.py
@@ -231,13 +231,17 @@ class ContainerAttribute(OrderedDeclaration):
return self.function(obj, containers)
-class SubFactory(OrderedDeclaration):
- """Base class for attributes based upon a sub-factory.
+class ParameteredAttribute(OrderedDeclaration):
+ """Base class for attributes expecting parameters.
Attributes:
- defaults (dict): Overrides to the defaults defined in the wrapped
- factory
- factory (base.Factory): the wrapped factory
+ defaults (dict): Default values for the paramters.
+ May be overridden by call-time parameters.
+
+ Class attributes:
+ CONTAINERS_FIELD (str): name of the field, if any, where container
+ information (e.g for SubFactory) should be stored. If empty,
+ containers data isn't merged into generate() parameters.
"""
CONTAINERS_FIELD = '__containers'
@@ -248,7 +252,6 @@ class SubFactory(OrderedDeclaration):
def __init__(self, **kwargs):
super(ParameteredAttribute, self).__init__()
self.defaults = kwargs
- self.factory = factory
def _prepare_containers(self, obj, containers=()):
if self.EXTEND_CONTAINERS:
@@ -260,19 +263,17 @@ class SubFactory(OrderedDeclaration):
"""Evaluate the current definition and fill its attributes.
Uses attributes definition in the following order:
- - attributes defined in the wrapped factory class
- - values defined when defining the SubFactory
- - additional values defined in attributes
+ - values defined when defining the ParameteredAttribute
+ - additional values defined when instantiating the containing factory
Args:
- create (bool): whether the subfactory should call 'build' or
- 'create'
+ create (bool): whether the parent factory is being 'built' or
+ 'created'
extra (containers.DeclarationDict): extra values that should
- override the wrapped factory's defaults
+ override the defaults
containers (list of LazyStub): List of LazyStub for the chain of
factories being evaluated, the calling stub being first.
"""
-
defaults = dict(self.defaults)
if extra:
defaults.update(extra)
@@ -393,7 +394,7 @@ class PostGenerationDeclaration(object):
kwargs = utils.extract_dict(name, attrs)
return extracted, kwargs
- def call(self, obj, create, extracted=None, **kwargs):
+ def call(self, obj, create, extracted=None, **kwargs): # pragma: no cover
"""Call this hook; no return value is expected.
Args:
diff --git a/factory/utils.py b/factory/utils.py
index f845f46..fb8cfef 100644
--- a/factory/utils.py
+++ b/factory/utils.py
@@ -58,20 +58,16 @@ def extract_dict(prefix, kwargs, pop=True, exclude=()):
return extracted
-def declength_compare(a, b):
- """Compare objects, choosing longest first."""
- if len(a) > len(b):
- return -1
- elif len(a) < len(b):
- return 1
- else:
- return cmp(a, b)
-
-
def multi_extract_dict(prefixes, kwargs, pop=True, exclude=()):
"""Extracts all values from a given list of prefixes.
- Arguments have the same meaning as for extract_dict.
+ Extraction will start with longer prefixes.
+
+ Args:
+ prefixes (str list): the prefixes to use for lookups
+ kwargs (dict): the dict from which values should be extracted
+ pop (bool): whether to use pop (True) or get (False)
+ exclude (iterable): list of prefixed keys that shouldn't be extracted
Returns:
dict(str => dict): a dict mapping each prefix to the dict of extracted
@@ -79,10 +75,22 @@ def multi_extract_dict(prefixes, kwargs, pop=True, exclude=()):
"""
results = {}
exclude = list(exclude)
- for prefix in sorted(prefixes, cmp=declength_compare):
+ for prefix in sorted(prefixes, key=lambda x: -len(x)):
extracted = extract_dict(prefix, kwargs, pop=pop, exclude=exclude)
results[prefix] = extracted
exclude.extend(
['%s%s%s' % (prefix, ATTR_SPLITTER, key) for key in extracted])
return results
+
+
+def import_object(module_name, attribute_name):
+ """Import an object from its absolute path.
+
+ Example:
+ >>> import_object('datetime', 'datetime')
+ <type 'datetime.datetime'>
+ """
+ module = __import__(module_name, {}, {}, [attribute_name], 0)
+ return getattr(module, attribute_name)
+
diff --git a/setup.py b/setup.py
index 2a43ed4..050e43b 100755
--- a/setup.py
+++ b/setup.py
@@ -1,12 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+import os
+import re
import sys
from distutils.core import setup
from distutils import cmd
-# Remember to change in factory/__init__.py as well!
-VERSION = '1.1.5'
+root = os.path.abspath(os.path.dirname(__file__))
+
+def get_version(*module_dir_components):
+ version_re = re.compile(r"^__version__ = ['\"](.*)['\"]$")
+ module_root = os.path.join(root, *module_dir_components)
+ module_init = os.path.join(module_root, '__init__.py')
+ with open(module_init, 'r') as f:
+ for line in f:
+ match = version_re.match(line[:-1])
+ if match:
+ return match.groups()[0]
+ return '0.1.0'
+
+VERSION = get_version('factory')
class test(cmd.Command):
diff --git a/tests/cyclic/__init__.py b/tests/cyclic/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/cyclic/__init__.py
diff --git a/tests/cyclic/bar.py b/tests/cyclic/bar.py
new file mode 100644
index 0000000..fed0602
--- /dev/null
+++ b/tests/cyclic/bar.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2011-2013 Raphaël Barrois
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""Helper to test circular factory dependencies."""
+
+import factory
+
+class Bar(object):
+ def __init__(self, foo, y):
+ self.foo = foo
+ self.y = y
+
+
+class BarFactory(factory.Factory):
+ FACTORY_FOR = Bar
+
+ y = 13
+ foo = factory.SubFactory('cyclic.foo.FooFactory')
+
diff --git a/tests/cyclic/foo.py b/tests/cyclic/foo.py
new file mode 100644
index 0000000..7f00f12
--- /dev/null
+++ b/tests/cyclic/foo.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2011-2013 Raphaël Barrois
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""Helper to test circular factory dependencies."""
+
+import factory
+
+from cyclic import bar
+
+class Foo(object):
+ def __init__(self, bar, x):
+ self.bar = bar
+ self.x = x
+
+
+class FooFactory(factory.Factory):
+ FACTORY_FOR = Foo
+
+ x = 42
+ bar = factory.SubFactory(bar.BarFactory)
diff --git a/tests/test_base.py b/tests/test_base.py
index 216711a..73e59fa 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -35,19 +35,25 @@ class TestObject(object):
self.four = four
class FakeDjangoModel(object):
- class FakeDjangoManager(object):
- def create(self, **kwargs):
- fake_model = FakeDjangoModel(**kwargs)
- fake_model.id = 1
- return fake_model
-
- objects = FakeDjangoManager()
+ @classmethod
+ def create(cls, **kwargs):
+ instance = cls(**kwargs)
+ instance.id = 1
+ return instance
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
self.id = None
+class FakeModelFactory(base.Factory):
+ ABSTRACT_FACTORY = True
+
+ @classmethod
+ def _create(cls, target_class, *args, **kwargs):
+ return target_class.create(**kwargs)
+
+
class TestModel(FakeDjangoModel):
pass
@@ -84,6 +90,8 @@ class FactoryTestCase(unittest.TestCase):
def test_lazy_attribute_non_existent_param(self):
class TestObjectFactory(base.Factory):
+ FACTORY_FOR = TestObject
+
one = declarations.LazyAttribute(lambda a: a.does_not_exist )
self.assertRaises(AttributeError, TestObjectFactory)
@@ -91,9 +99,13 @@ class FactoryTestCase(unittest.TestCase):
def test_inheritance_with_sequence(self):
"""Tests that sequence IDs are shared between parent and son."""
class TestObjectFactory(base.Factory):
+ FACTORY_FOR = TestObject
+
one = declarations.Sequence(lambda a: a)
class TestSubFactory(TestObjectFactory):
+ FACTORY_FOR = TestObject
+
pass
parent = TestObjectFactory.build()
@@ -115,6 +127,8 @@ class FactoryDefaultStrategyTestCase(unittest.TestCase):
base.Factory.FACTORY_STRATEGY = base.BUILD_STRATEGY
class TestModelFactory(base.Factory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory()
@@ -124,7 +138,9 @@ class FactoryDefaultStrategyTestCase(unittest.TestCase):
def test_create_strategy(self):
# Default FACTORY_STRATEGY
- class TestModelFactory(base.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory()
@@ -135,6 +151,8 @@ class FactoryDefaultStrategyTestCase(unittest.TestCase):
base.Factory.FACTORY_STRATEGY = base.STUB_STRATEGY
class TestModelFactory(base.Factory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory()
@@ -145,12 +163,16 @@ class FactoryDefaultStrategyTestCase(unittest.TestCase):
base.Factory.FACTORY_STRATEGY = 'unknown'
class TestModelFactory(base.Factory):
+ FACTORY_FOR = TestModel
+
one = 'one'
self.assertRaises(base.Factory.UnknownStrategy, TestModelFactory)
def test_stub_with_non_stub_strategy(self):
class TestModelFactory(base.StubFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
TestModelFactory.FACTORY_STRATEGY = base.CREATE_STRATEGY
@@ -163,6 +185,8 @@ class FactoryDefaultStrategyTestCase(unittest.TestCase):
def test_change_strategy(self):
@base.use_strategy(base.CREATE_STRATEGY)
class TestModelFactory(base.StubFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
self.assertEqual(base.CREATE_STRATEGY, TestModelFactory.FACTORY_STRATEGY)
@@ -183,6 +207,8 @@ class FactoryCreationTestCase(unittest.TestCase):
def test_inheritance_with_stub(self):
class TestObjectFactory(base.StubFactory):
+ FACTORY_FOR = TestObject
+
pass
class TestFactory(TestObjectFactory):
@@ -224,12 +250,16 @@ class PostGenerationParsingTestCase(unittest.TestCase):
def test_extraction(self):
class TestObjectFactory(base.Factory):
+ FACTORY_FOR = TestObject
+
foo = declarations.PostGenerationDeclaration()
self.assertIn('foo', TestObjectFactory._postgen_declarations)
def test_classlevel_extraction(self):
class TestObjectFactory(base.Factory):
+ FACTORY_FOR = TestObject
+
foo = declarations.PostGenerationDeclaration()
foo__bar = 42
diff --git a/tests/test_containers.py b/tests/test_containers.py
index 7c8d829..70ed885 100644
--- a/tests/test_containers.py
+++ b/tests/test_containers.py
@@ -178,6 +178,17 @@ class DeclarationDictTestCase(unittest.TestCase):
self.assertEqual(set(['one', 'three']), set(d))
self.assertEqual(set([1, 3]), set(d.values()))
+ def test_update_with_public_ignores_factory_attributes(self):
+ """Ensure that a DeclarationDict ignores FACTORY_ keys."""
+ d = containers.DeclarationDict()
+ d.update_with_public({
+ 'one': 1,
+ 'FACTORY_FOR': 2,
+ 'FACTORY_ARG_PARAMETERS': 3,
+ })
+ self.assertEqual(['one'], list(d))
+ self.assertEqual([1], list(d.values()))
+
class AttributeBuilderTestCase(unittest.TestCase):
def test_empty(self):
diff --git a/tests/test_using.py b/tests/test_using.py
index 497e206..821fad3 100644
--- a/tests/test_using.py
+++ b/tests/test_using.py
@@ -21,6 +21,11 @@
"""Tests using factory."""
+import functools
+import os
+import sys
+import warnings
+
import factory
from .compat import is_python2, unittest
@@ -71,7 +76,16 @@ class FakeModel(object):
setattr(self, name, value)
self.id = None
-class TestModel(FakeDjangoModel):
+
+class FakeModelFactory(factory.Factory):
+ ABSTRACT_FACTORY = True
+
+ @classmethod
+ def _create(cls, target_class, *args, **kwargs):
+ return target_class.create(**kwargs)
+
+
+class TestModel(FakeModel):
pass
@@ -116,7 +130,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_create_batch(self):
- objs = factory.create_batch(FakeDjangoModel, 4, foo='bar')
+ objs = factory.create_batch(FakeModel, 4, foo='bar')
self.assertEqual(4, len(objs))
self.assertEqual(4, len(set(objs)))
@@ -142,7 +156,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertFalse(hasattr(obj, 'two'))
def test_stub_batch(self):
- objs = factory.stub_batch(FakeDjangoModel, 4, foo='bar')
+ objs = factory.stub_batch(FakeModel, 4, foo='bar')
self.assertEqual(4, len(objs))
self.assertEqual(4, len(set(objs)))
@@ -152,7 +166,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_generate_build(self):
- obj = factory.generate(FakeDjangoModel, factory.BUILD_STRATEGY, foo='bar')
+ obj = factory.generate(FakeModel, factory.BUILD_STRATEGY, foo='bar')
self.assertEqual(obj.id, None)
self.assertEqual(obj.foo, 'bar')
@@ -168,12 +182,12 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_generate_stub(self):
- obj = factory.generate(FakeDjangoModel, factory.STUB_STRATEGY, foo='bar')
+ obj = factory.generate(FakeModel, factory.STUB_STRATEGY, foo='bar')
self.assertFalse(hasattr(obj, 'id'))
self.assertEqual(obj.foo, 'bar')
def test_generate_batch_build(self):
- objs = factory.generate_batch(FakeDjangoModel, factory.BUILD_STRATEGY, 20, foo='bar')
+ objs = factory.generate_batch(FakeModel, factory.BUILD_STRATEGY, 20, foo='bar')
self.assertEqual(20, len(objs))
self.assertEqual(20, len(set(objs)))
@@ -183,7 +197,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_generate_batch_create(self):
- objs = factory.generate_batch(FakeDjangoModel, factory.CREATE_STRATEGY, 20, foo='bar')
+ objs = factory.generate_batch(FakeModel, factory.CREATE_STRATEGY, 20, foo='bar')
self.assertEqual(20, len(objs))
self.assertEqual(20, len(set(objs)))
@@ -204,7 +218,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_generate_batch_stub(self):
- objs = factory.generate_batch(FakeDjangoModel, factory.STUB_STRATEGY, 20, foo='bar')
+ objs = factory.generate_batch(FakeModel, factory.STUB_STRATEGY, 20, foo='bar')
self.assertEqual(20, len(objs))
self.assertEqual(20, len(set(objs)))
@@ -214,7 +228,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_simple_generate_build(self):
- obj = factory.simple_generate(FakeDjangoModel, False, foo='bar')
+ obj = factory.simple_generate(FakeModel, False, foo='bar')
self.assertEqual(obj.id, None)
self.assertEqual(obj.foo, 'bar')
@@ -229,7 +243,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_simple_generate_batch_build(self):
- objs = factory.simple_generate_batch(FakeDjangoModel, False, 20, foo='bar')
+ objs = factory.simple_generate_batch(FakeModel, False, 20, foo='bar')
self.assertEqual(20, len(objs))
self.assertEqual(20, len(set(objs)))
@@ -239,7 +253,7 @@ class SimpleBuildTestCase(unittest.TestCase):
self.assertEqual(obj.foo, 'bar')
def test_simple_generate_batch_create(self):
- objs = factory.simple_generate_batch(FakeDjangoModel, True, 20, foo='bar')
+ objs = factory.simple_generate_batch(FakeModel, True, 20, foo='bar')
self.assertEqual(20, len(objs))
self.assertEqual(20, len(set(objs)))
@@ -278,6 +292,8 @@ class SimpleBuildTestCase(unittest.TestCase):
class UsingFactoryTestCase(unittest.TestCase):
def test_attribute(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 'one'
test_object = TestObjectFactory.build()
@@ -321,6 +337,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_sequence_custom_begin(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
@classmethod
def _setup_next_sequence(cls):
return 42
@@ -402,6 +420,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_lazy_attribute(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = factory.LazyAttribute(lambda a: 'abc' )
two = factory.LazyAttribute(lambda a: a.one + ' xyz')
@@ -426,6 +446,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_lazy_attribute_decorator(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
@factory.lazy_attribute
def one(a):
return 'one'
@@ -438,6 +460,8 @@ class UsingFactoryTestCase(unittest.TestCase):
n = 3
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 'xx'
two = factory.SelfAttribute('one')
three = TmpObj()
@@ -469,6 +493,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_sequence_decorator(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
@factory.sequence
def one(n):
return 'one%d' % n
@@ -478,6 +504,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_lazy_attribute_sequence_decorator(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
@factory.lazy_attribute_sequence
def one(a, n):
return 'one%d' % n
@@ -516,7 +544,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertTrue(test_model.id)
def test_create_batch(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
objs = TestModelFactory.create_batch(20, two=factory.Sequence(int))
@@ -530,7 +560,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertTrue(obj.id)
def test_generate_build(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory.generate(factory.BUILD_STRATEGY)
@@ -538,7 +570,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertFalse(test_model.id)
def test_generate_create(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory.generate(factory.CREATE_STRATEGY)
@@ -546,7 +580,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertTrue(test_model.id)
def test_generate_stub(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory.generate(factory.STUB_STRATEGY)
@@ -554,7 +590,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertFalse(hasattr(test_model, 'id'))
def test_generate_batch_build(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
objs = TestModelFactory.generate_batch(factory.BUILD_STRATEGY, 20, two='two')
@@ -568,7 +606,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertFalse(obj.id)
def test_generate_batch_create(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
objs = TestModelFactory.generate_batch(factory.CREATE_STRATEGY, 20, two='two')
@@ -582,7 +622,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertTrue(obj.id)
def test_generate_batch_stub(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
objs = TestModelFactory.generate_batch(factory.STUB_STRATEGY, 20, two='two')
@@ -596,7 +638,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertFalse(hasattr(obj, 'id'))
def test_simple_generate_build(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory.simple_generate(False)
@@ -604,7 +648,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertFalse(test_model.id)
def test_simple_generate_create(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
test_model = TestModelFactory.simple_generate(True)
@@ -612,7 +658,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertTrue(test_model.id)
def test_simple_generate_batch_build(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
objs = TestModelFactory.simple_generate_batch(False, 20, two='two')
@@ -626,7 +674,9 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertFalse(obj.id)
def test_simple_generate_batch_create(self):
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+
one = 'one'
objs = TestModelFactory.simple_generate_batch(True, 20, two='two')
@@ -641,6 +691,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_stub_batch(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 'one'
two = factory.LazyAttribute(lambda a: a.one + ' two')
three = factory.Sequence(lambda n: int(n))
@@ -658,6 +710,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_inheritance(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 'one'
two = factory.LazyAttribute(lambda a: a.one + ' two')
@@ -678,6 +732,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_inheritance_with_inherited_class(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 'one'
two = factory.LazyAttribute(lambda a: a.one + ' two')
@@ -693,6 +749,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_dual_inheritance(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 'one'
class TestOtherFactory(factory.Factory):
@@ -711,6 +769,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_class_method_accessible(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
@classmethod
def alt_create(cls, **kwargs):
return kwargs
@@ -719,6 +779,8 @@ class UsingFactoryTestCase(unittest.TestCase):
def test_static_method_accessible(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
@staticmethod
def alt_create(**kwargs):
return kwargs
@@ -861,11 +923,11 @@ class SubFactoryTestCase(unittest.TestCase):
class TestModel2(FakeModel):
pass
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
FACTORY_FOR = TestModel
one = 3
- class TestModel2Factory(factory.Factory):
+ class TestModel2Factory(FakeModelFactory):
FACTORY_FOR = TestModel2
two = factory.SubFactory(TestModelFactory, one=1)
@@ -878,10 +940,10 @@ class SubFactoryTestCase(unittest.TestCase):
class TestModel2(FakeModel):
pass
- class TestModelFactory(factory.Factory):
+ class TestModelFactory(FakeModelFactory):
FACTORY_FOR = TestModel
- class TestModel2Factory(factory.Factory):
+ class TestModel2Factory(FakeModelFactory):
FACTORY_FOR = TestModel2
two = factory.SubFactory(TestModelFactory,
one=factory.Sequence(lambda n: 'x%dx' % n),
@@ -1073,6 +1135,78 @@ class SubFactoryTestCase(unittest.TestCase):
self.assertEqual(outer.side_a.inner_from_a.a, outer.foo * 2)
self.assertEqual(outer.side_a.inner_from_a.b, 4)
+ def test_nonstrict_container_attribute(self):
+ class TestModel2(FakeModel):
+ pass
+
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+ one = 3
+ two = factory.ContainerAttribute(lambda obj, containers: len(containers or []), strict=False)
+
+ class TestModel2Factory(FakeModelFactory):
+ FACTORY_FOR = TestModel2
+ one = 1
+ two = factory.SubFactory(TestModelFactory, one=1)
+
+ obj = TestModel2Factory.build()
+ self.assertEqual(1, obj.one)
+ self.assertEqual(1, obj.two.one)
+ self.assertEqual(1, obj.two.two)
+
+ obj = TestModelFactory()
+ self.assertEqual(3, obj.one)
+ self.assertEqual(0, obj.two)
+
+ def test_strict_container_attribute(self):
+ class TestModel2(FakeModel):
+ pass
+
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+ one = 3
+ two = factory.ContainerAttribute(lambda obj, containers: len(containers or []), strict=True)
+
+ class TestModel2Factory(FakeModelFactory):
+ FACTORY_FOR = TestModel2
+ one = 1
+ two = factory.SubFactory(TestModelFactory, one=1)
+
+ obj = TestModel2Factory.build()
+ self.assertEqual(1, obj.one)
+ self.assertEqual(1, obj.two.one)
+ self.assertEqual(1, obj.two.two)
+
+ self.assertRaises(TypeError, TestModelFactory.build)
+
+ def test_function_container_attribute(self):
+ class TestModel2(FakeModel):
+ pass
+
+ class TestModelFactory(FakeModelFactory):
+ FACTORY_FOR = TestModel
+ one = 3
+
+ @factory.container_attribute
+ def two(self, containers):
+ if containers:
+ return len(containers)
+ return 42
+
+ class TestModel2Factory(FakeModelFactory):
+ FACTORY_FOR = TestModel2
+ one = 1
+ two = factory.SubFactory(TestModelFactory, one=1)
+
+ obj = TestModel2Factory.build()
+ self.assertEqual(1, obj.one)
+ self.assertEqual(1, obj.two.one)
+ self.assertEqual(1, obj.two.two)
+
+ obj = TestModelFactory()
+ self.assertEqual(3, obj.one)
+ self.assertEqual(42, obj.two)
+
class IteratorTestCase(unittest.TestCase):
@@ -1115,6 +1249,8 @@ class IteratorTestCase(unittest.TestCase):
def test_iterator_decorator(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
@factory.iterator
def one():
for i in range(10, 50):
@@ -1257,6 +1393,8 @@ class DjangoModelFactoryTestCase(unittest.TestCase):
class PostGenerationTestCase(unittest.TestCase):
def test_post_generation(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 1
@factory.post_generation
@@ -1294,6 +1432,8 @@ class PostGenerationTestCase(unittest.TestCase):
def test_post_generation_extraction(self):
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
one = 1
@factory.post_generation
@@ -1317,10 +1457,40 @@ class PostGenerationTestCase(unittest.TestCase):
self.assertEqual(kwargs, {'foo': 13})
class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+
bar = factory.PostGeneration(my_lambda)
obj = TestObjectFactory.build(bar=42, bar__foo=13)
+ def test_post_generation_method_call(self):
+ calls = []
+
+ class TestObject(object):
+ def __init__(self, one=None, two=None):
+ self.one = one
+ self.two = two
+ self.extra = None
+
+ def call(self, *args, **kwargs):
+ self.extra = (args, kwargs)
+
+ class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+ one = 3
+ two = 2
+ post_call = factory.PostGenerationMethodCall('call', one=1)
+
+ obj = TestObjectFactory.build()
+ self.assertEqual(3, obj.one)
+ self.assertEqual(2, obj.two)
+ self.assertEqual(((), {'one': 1}), obj.extra)
+
+ obj = TestObjectFactory.build(post_call__one=2, post_call__two=3)
+ self.assertEqual(3, obj.one)
+ self.assertEqual(2, obj.two)
+ self.assertEqual(((), {'one': 2, 'two': 3}), obj.extra)
+
def test_related_factory(self):
class TestRelatedObject(object):
def __init__(self, obj=None, one=None, two=None):
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 787164a..b353c9d 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -25,23 +25,6 @@ from factory import utils
from .compat import unittest
-class DecLengthCompareTestCase(unittest.TestCase):
- def test_reciprocity(self):
- self.assertEqual(1, utils.declength_compare('a', 'bb'))
- self.assertEqual(-1, utils.declength_compare('aa', 'b'))
-
- def test_not_lexical(self):
- self.assertEqual(1, utils.declength_compare('abc', 'aaaa'))
- self.assertEqual(-1, utils.declength_compare('aaaa', 'abc'))
-
- def test_same_length(self):
- self.assertEqual(-1, utils.declength_compare('abc', 'abd'))
- self.assertEqual(1, utils.declength_compare('abe', 'abd'))
-
- def test_equality(self):
- self.assertEqual(0, utils.declength_compare('abc', 'abc'))
- self.assertEqual(0, utils.declength_compare([1, 2, 3], [1, 2, 3]))
-
class ExtractDictTestCase(unittest.TestCase):
def test_empty_dict(self):
@@ -102,6 +85,7 @@ class ExtractDictTestCase(unittest.TestCase):
self.assertIn('foo__bar', d)
self.assertNotIn('foo__foo__bar', d)
+
class MultiExtractDictTestCase(unittest.TestCase):
def test_empty_dict(self):
self.assertEqual({'foo': {}}, utils.multi_extract_dict(['foo'], {}))
@@ -230,3 +214,19 @@ class MultiExtractDictTestCase(unittest.TestCase):
self.assertNotIn('foo__foo__bar', d)
self.assertNotIn('bar__foo', d)
self.assertNotIn('bar__bar__baz', d)
+
+
+class ImportObjectTestCase(unittest.TestCase):
+ def test_datetime(self):
+ imported = utils.import_object('datetime', 'date')
+ import datetime
+ d = datetime.date
+ self.assertEqual(d, imported)
+
+ def test_unknown_attribute(self):
+ self.assertRaises(AttributeError, utils.import_object,
+ 'datetime', 'foo')
+
+ def test_invalid_module(self):
+ self.assertRaises(ImportError, utils.import_object,
+ 'this-is-an-invalid-module', '__name__')