Permalink
Fetching contributors…
Cannot retrieve contributors at this time
391 lines (220 sloc) 10.9 KB

Fuzzy attributes

.. module:: factory.fuzzy

Note

Now that FactoryBoy includes the :class:`factory.Faker` class, most of these built-in fuzzers are deprecated in favor of their Faker equivalents. Further discussion here: https://github.com/FactoryBoy/factory_boy/issues/271/

Some tests may be interested in testing with fuzzy, random values.

This is handled by the :mod:`factory.fuzzy` module, which provides a few random declarations.

Note

Use import factory.fuzzy to load this module.

FuzzyAttribute

The :class:`FuzzyAttribute` uses an arbitrary callable as fuzzer. It is expected that successive calls of that function return various values.

.. attribute:: fuzzer

    The callable that generates random values

FuzzyText

The :class:`FuzzyText` fuzzer yields random strings beginning with the given :attr:`prefix`, followed by :attr:`length` charactes chosen from the :attr:`chars` character set, and ending with the given :attr:`suffix`.

.. attribute:: length

    int, the length of the random part

.. attribute:: prefix

    text, an optional prefix to prepend to the random part

.. attribute:: suffix

    text, an optional suffix to append to the random part

.. attribute:: chars

    char iterable, the chars to choose from; defaults to the list of ascii
        letters and numbers.

FuzzyChoice

The :class:`FuzzyChoice` fuzzer yields random choices from the given iterable.

Note

The passed in :attr:`choices` will be converted into a list upon first use, not at declaration time.

This allows passing in, for instance, a Django queryset that will only hit the database during the database, not at import time.

Warning

When using Python2 and list comprehension, use private variable names as in:

[_x.name for _x in items]

instead of:

[x.name for x in items]

.. attribute:: choices

    The list of choices to select randomly

FuzzyInteger

The :class:`FuzzyInteger` fuzzer generates random integers within a given inclusive range.

The :attr:`low` bound may be omitted, in which case it defaults to 0:

>>> fi = FuzzyInteger(0, 42)
>>> fi.low, fi.high
0, 42

>>> fi = FuzzyInteger(42)
>>> fi.low, fi.high
0, 42
.. attribute:: low

    int, the inclusive lower bound of generated integers

.. attribute:: high

    int, the inclusive higher bound of generated integers

.. attribute:: step

    int, the step between values in the range; for instance, a ``FuzzyInteger(0, 42, step=3)``
    might only yield values from ``[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42]``.

FuzzyDecimal

The :class:`FuzzyDecimal` fuzzer generates random :class:`decimals <decimal.Decimal>` within a given inclusive range.

The :attr:`low` bound may be omitted, in which case it defaults to 0:

>>> FuzzyDecimal(0.5, 42.7)
>>> fi.low, fi.high
0.5, 42.7

>>> fi = FuzzyDecimal(42.7)
>>> fi.low, fi.high
0.0, 42.7

>>> fi = FuzzyDecimal(0.5, 42.7, 3)
>>> fi.low, fi.high, fi.precision
0.5, 42.7, 3
.. attribute:: low

    decimal, the inclusive lower bound of generated decimals

.. attribute:: high

    decimal, the inclusive higher bound of generated decimals

.. attribute:: precision
    int, the number of digits to generate after the dot. The default is 2 digits.

FuzzyFloat

The :class:`FuzzyFloat` fuzzer provides random :class:`float` objects within a given inclusive range.

>>> FuzzyFloat(0.5, 42.7)
>>> fi.low, fi.high
0.5, 42.7

>>> fi = FuzzyFloat(42.7)
>>> fi.low, fi.high
0.0, 42.7
.. attribute:: low

    decimal, the inclusive lower bound of generated floats

.. attribute:: high

    decimal, the inclusive higher bound of generated floats

FuzzyDate

The :class:`FuzzyDate` fuzzer generates random dates within a given inclusive range.

The :attr:`end_date` bound may be omitted, in which case it defaults to the current date:

>>> fd = FuzzyDate(datetime.date(2008, 1, 1))
>>> fd.start_date, fd.end_date
datetime.date(2008, 1, 1), datetime.date(2013, 4, 16)
.. attribute:: start_date

    :class:`datetime.date`, the inclusive lower bound of generated dates

.. attribute:: end_date

    :class:`datetime.date`, the inclusive higher bound of generated dates

FuzzyDateTime

The :class:`FuzzyDateTime` fuzzer generates random timezone-aware datetime within a given inclusive range.

The :attr:`end_dt` bound may be omitted, in which case it defaults to datetime.datetime.now() localized into the UTC timezone.

>>> fdt = FuzzyDateTime(datetime.datetime(2008, 1, 1, tzinfo=UTC))
>>> fdt.start_dt, fdt.end_dt
datetime.datetime(2008, 1, 1, tzinfo=UTC), datetime.datetime(2013, 4, 21, 19, 13, 32, 458487, tzinfo=UTC)

The force_XXX keyword arguments force the related value of generated datetimes:

>>> fdt = FuzzyDateTime(datetime.datetime(2008, 1, 1, tzinfo=UTC), datetime.datetime(2009, 1, 1, tzinfo=UTC),
...     force_day=3, force_second=42)
>>> fdt.evaluate(2, None, False)  # Actual code used by ``SomeFactory.build()``
datetime.datetime(2008, 5, 3, 12, 13, 42, 124848, tzinfo=UTC)
.. attribute:: start_dt

    :class:`datetime.datetime`, the inclusive lower bound of generated datetimes

.. attribute:: end_dt

    :class:`datetime.datetime`, the inclusive upper bound of generated datetimes


.. attribute:: force_year

    int or None; if set, forces the :attr:`~datetime.datetime.year` of generated datetime.

.. attribute:: force_month

    int or None; if set, forces the :attr:`~datetime.datetime.month` of generated datetime.

.. attribute:: force_day

    int or None; if set, forces the :attr:`~datetime.datetime.day` of generated datetime.

.. attribute:: force_hour

    int or None; if set, forces the :attr:`~datetime.datetime.hour` of generated datetime.

.. attribute:: force_minute

    int or None; if set, forces the :attr:`~datetime.datetime.minute` of generated datetime.

.. attribute:: force_second

    int or None; if set, forces the :attr:`~datetime.datetime.second` of generated datetime.

.. attribute:: force_microsecond

    int or None; if set, forces the :attr:`~datetime.datetime.microsecond` of generated datetime.

FuzzyNaiveDateTime

The :class:`FuzzyNaiveDateTime` fuzzer generates random naive datetime within a given inclusive range.

The :attr:`end_dt` bound may be omitted, in which case it defaults to datetime.datetime.now():

>>> fdt = FuzzyNaiveDateTime(datetime.datetime(2008, 1, 1))
>>> fdt.start_dt, fdt.end_dt
datetime.datetime(2008, 1, 1), datetime.datetime(2013, 4, 21, 19, 13, 32, 458487)

The force_XXX keyword arguments force the related value of generated datetimes:

>>> fdt = FuzzyNaiveDateTime(datetime.datetime(2008, 1, 1), datetime.datetime(2009, 1, 1),
...     force_day=3, force_second=42)
>>> fdt.evaluate(2, None, False)  # Actual code used by ``SomeFactory.build()``
datetime.datetime(2008, 5, 3, 12, 13, 42, 124848)
.. attribute:: start_dt

    :class:`datetime.datetime`, the inclusive lower bound of generated datetimes

.. attribute:: end_dt

    :class:`datetime.datetime`, the inclusive upper bound of generated datetimes


.. attribute:: force_year

    int or None; if set, forces the :attr:`~datetime.datetime.year` of generated datetime.

.. attribute:: force_month

    int or None; if set, forces the :attr:`~datetime.datetime.month` of generated datetime.

.. attribute:: force_day

    int or None; if set, forces the :attr:`~datetime.datetime.day` of generated datetime.

.. attribute:: force_hour

    int or None; if set, forces the :attr:`~datetime.datetime.hour` of generated datetime.

.. attribute:: force_minute

    int or None; if set, forces the :attr:`~datetime.datetime.minute` of generated datetime.

.. attribute:: force_second

    int or None; if set, forces the :attr:`~datetime.datetime.second` of generated datetime.

.. attribute:: force_microsecond

    int or None; if set, forces the :attr:`~datetime.datetime.microsecond` of generated datetime.

Custom fuzzy fields

Alternate fuzzy fields may be defined. They should inherit from the :class:`BaseFuzzyAttribute` class, and override its :meth:`~BaseFuzzyAttribute.fuzz` method.

Base class for all fuzzy attributes.

.. method:: fuzz(self)

    The method responsible for generating random values.
    *Must* be overridden in subclasses.

Managing randomness

Using :mod:`random` in factories allows to "fuzz" a program efficiently. However, it's sometimes required to reproduce a failing test.

:mod:`factory.fuzzy` uses a separate instance of :class:`random.Random`, and provides a few helpers for this:

.. method:: get_random_state()

    Call :meth:`get_random_state` to retrieve the random generator's current
    state.

.. method:: set_random_state(state)

    Use :meth:`set_random_state` to set a custom state into the random generator
    (fetched from :meth:`get_random_state` in a previous run, for instance)

.. method:: reseed_random(seed)

    The :meth:`reseed_random` function allows to load a chosen seed into the random generator.


Custom :class:`BaseFuzzyAttribute` subclasses SHOULD use :obj:`factory.fuzzy._random` as a randomness source; this ensures that data they generate can be regenerated using the simple state from :meth:`get_random_state`.