summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/changelog.rst5
-rw-r--r--docs/ideas.rst3
-rw-r--r--docs/reference.rst31
-rw-r--r--factory/base.py7
-rw-r--r--tests/test_using.py59
5 files changed, 101 insertions, 4 deletions
diff --git a/docs/changelog.rst b/docs/changelog.rst
index b9ea79f..9a4a64e 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -10,8 +10,11 @@ ChangeLog
- 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>`_)
- - Add support for ``get_or_create`` in :class:`~factory.DjangoModelFactory`
- 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`
*Removed:*
diff --git a/docs/ideas.rst b/docs/ideas.rst
index 004f722..914e640 100644
--- a/docs/ideas.rst
+++ b/docs/ideas.rst
@@ -5,7 +5,4 @@ Ideas
This is a list of future features that may be incorporated into factory_boy:
* **A 'options' attribute**: instead of adding more class-level constants, use a django-style ``class Meta`` Factory attribute with all options there
-* **factory-local fields**: Allow some fields to be available while building attributes,
- but not passed to the associated class.
- For instance, a ``global_kind`` field would be used to select values for many other fields.
diff --git a/docs/reference.rst b/docs/reference.rst
index d5b14a9..a2af327 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -50,6 +50,37 @@ The :class:`Factory` class
<User: john>
>>> User('john', 'john@example.com', firstname="John") # actual call
+ .. attribute:: FACTORY_HIDDEN_ARGS
+
+ While writing a :class:`Factory` for some object, it may be useful to
+ have general fields helping defining others, but that should not be
+ passed to the target class; for instance, a field named 'now' that would
+ hold a reference time used by other objects.
+
+ Factory fields whose name are listed in :attr:`FACTORY_HIDDEN_ARGS` will
+ be removed from the set of args/kwargs passed to the underlying class;
+ they can be any valid factory_boy declaration:
+
+ .. code-block:: python
+
+ class OrderFactory(factory.Factory):
+ FACTORY_FOR = Order
+ FACTORY_HIDDEN_ARGS = ('now',)
+
+ now = factory.LazyAttribute(lambda o: datetime.datetime.utcnow())
+ started_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(hours=1))
+ paid_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(minutes=50))
+
+ .. code-block:: pycon
+
+ >>> OrderFactory() # The value of 'now' isn't passed to Order()
+ <Order: started 2013-04-01 12:00:00, paid 2013-04-01 12:10:00>
+
+ >>> # An alternate value may be passed for 'now'
+ >>> OrderFactory(now=datetime.datetime(2013, 4, 1, 10))
+ <Order: started 2013-04-01 09:00:00, paid 2013-04-01 09:10:00>
+
+
**Base functions:**
The :class:`Factory` class provides a few methods for getting objects;
diff --git a/factory/base.py b/factory/base.py
index ff3e558..58cd50b 100644
--- a/factory/base.py
+++ b/factory/base.py
@@ -234,6 +234,9 @@ class BaseFactory(object):
# List of arguments that should be passed as *args instead of **kwargs
FACTORY_ARG_PARAMETERS = ()
+ # List of attributes that should not be passed to the underlying class
+ FACTORY_HIDDEN_ARGS = ()
+
@classmethod
def _setup_next_sequence(cls):
"""Set up an initial sequence value for Sequence attributes.
@@ -305,6 +308,10 @@ class BaseFactory(object):
target_class = getattr(cls, CLASS_ATTRIBUTE_ASSOCIATED_CLASS)
kwargs = cls._adjust_kwargs(**kwargs)
+ # Remove 'hidden' arguments.
+ for arg in cls.FACTORY_HIDDEN_ARGS:
+ del kwargs[arg]
+
# Extract *args from **kwargs
args = tuple(kwargs.pop(key) for key in cls.FACTORY_ARG_PARAMETERS)
diff --git a/tests/test_using.py b/tests/test_using.py
index 2e07621..41b666f 100644
--- a/tests/test_using.py
+++ b/tests/test_using.py
@@ -762,6 +762,65 @@ class UsingFactoryTestCase(unittest.TestCase):
self.assertEqual(TestObjectFactory.alt_create(foo=1), {"foo": 1})
+ def test_arg_parameters(self):
+ class TestObject(object):
+ def __init__(self, *args, **kwargs):
+ self.args = args
+ self.kwargs = kwargs
+
+ class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+ FACTORY_ARG_PARAMETERS = ('x', 'y')
+
+ x = 1
+ y = 2
+ z = 3
+ t = 4
+
+ obj = TestObjectFactory.build(x=42, z=5)
+ self.assertEqual((42, 2), obj.args)
+ self.assertEqual({'z': 5, 't': 4}, obj.kwargs)
+
+ def test_hidden_args(self):
+ class TestObject(object):
+ def __init__(self, *args, **kwargs):
+ self.args = args
+ self.kwargs = kwargs
+
+ class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+ FACTORY_HIDDEN_ARGS = ('x', 'z')
+
+ x = 1
+ y = 2
+ z = 3
+ t = 4
+
+ obj = TestObjectFactory.build(x=42, z=5)
+ self.assertEqual((), obj.args)
+ self.assertEqual({'y': 2, 't': 4}, obj.kwargs)
+
+ def test_hidden_args_and_arg_parameters(self):
+ class TestObject(object):
+ def __init__(self, *args, **kwargs):
+ self.args = args
+ self.kwargs = kwargs
+
+ class TestObjectFactory(factory.Factory):
+ FACTORY_FOR = TestObject
+ FACTORY_HIDDEN_ARGS = ('x', 'z')
+ FACTORY_ARG_PARAMETERS = ('y',)
+
+ x = 1
+ y = 2
+ z = 3
+ t = 4
+
+ obj = TestObjectFactory.build(x=42, z=5)
+ self.assertEqual((2,), obj.args)
+ self.assertEqual({'t': 4}, obj.kwargs)
+
+
class NonKwargParametersTestCase(unittest.TestCase):
def test_build(self):