10

How can I set the default locale in Python's factory_boy for all of my Factories?

In docs says that one should set it with factory.Faker.override_default_locale but that does nothing to my fakers...

import factory
from app.models import Example
from custom_fakers import CustomFakers

# I use custom fakers, this indeed are added
factory.Faker.add_provider(CustomFakers)
# But not default locales
factory.Faker.override_default_locale('es_ES')

class ExampleFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Example

    name = factory.Faker('first_name')


>>> from example import ExampleFactory
>>> e1 = ExampleFactory()
>>> e1.name
>>> u'Chad'
Felix
  • 579
  • 1
  • 8
  • 23

5 Answers5

13

The Faker.override_default_locale() is a context manager, although it's not very clear from the docs.

As such, to change the default locale for a part of a test:

with factory.Faker.override_default_locale('es_ES'):
    ExampleFactory()

For the whole test:

@factory.Faker.override_default_locale('es_ES')
def test_foo(self):
    user = ExampleFactory()

For all the tests (Django):

# settings.py
TEST_RUNNER = 'myproject.testing.MyTestRunner'

# myproject/testing.py
import factory
from django.conf import settings
from django.util import translation
import django.test.runner

class MyTestRunner(django.test.runner.DiscoverRunner):
    def run_tests(self, test_labels, extra_tests=None, **kwargs):
        with factory.Faker.override_default_locale(translation.to_locale(settings.LANGUAGE_CODE)):
            return super().run_tests(test_labels, extra_tests=extra_tests, **kwargs)

More on it here.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Xelnor
  • 3,194
  • 12
  • 14
8

UPD As I said, this solution is suboptimal:

  • factory.Faker._DEFAULT_LOCALE is a private field
  • fake() and faker() use the private interface
  • fake() doesn't work since factory-boy==3.1.0
  • if I were to use faker, I'd use it directly, not via factory-boy

You should generally prefer the other answer. Leaving this one for posterity.


Not a good solution, but for now it's as good as it gets. You can change the variable that holds the value:

import factory
factory.Faker._DEFAULT_LOCALE = 'xx_XX'

Moreover, you can create a file like this (app/faker.py):

import factory
from faker.providers import BaseProvider

factory.Faker._DEFAULT_LOCALE = 'xx_XX'

def fake(name):
    return factory.Faker(name).generate({})

def faker():
    return factory.Faker._get_faker()

class MyProvider(BaseProvider):
    def category_name(self):
        return self.random_element(category_names)
    ...
factory.Faker.add_provider(MyProvider)

category_names = [...]

Then, once you import the file, the locale changes. Also, you get your providers and an easy way to use factory_boy's faker outside of the factories:

from app.faker import fake
print(fake('random_int'))
print(faker().random_int())
x-yuri
  • 16,722
  • 15
  • 114
  • 161
  • 1
    Faker 5.6.5 (and possibly all versions > 4.17.1) doesn't have `generate()` but have `evaluate(None, None, {"locale": None})`. – frost-nzcr4 Jan 25 '21 at 15:33
  • @frost-nzcr4 `generate()` belonged to [`factory-boy`](https://github.com/FactoryBoy/factory_boy/blob/3.0.1/factory/faker.py#L44), not `faker`, but since `factory-boy==3.1.0` the [interface changed](https://github.com/FactoryBoy/factory_boy/commit/f0a4ef008f07f8d42221565d8c33b88083f0be6d#diff-f57bbc6d43cc8512bb7a1ec249ce840ef810026b67792af1b4d2840c0b914bd8R46). See my updated answer. – x-yuri Jan 25 '21 at 19:44
3

I'm having same issue as yours. For a temporary solution try passing locale in factory.Faker.

For example:

name = factory.Faker('first_name', locale='es_ES')
  • yup, I know, but is not what I wanted to do :( devs of the project aren't too responsive this days neither :( – Felix Aug 25 '17 at 03:17
3

With Django, you can simply insert the following lines in <myproject>/settings.py:

import factory
factory.Faker._DEFAULT_LOCALE = 'fr_FR'
Benoit Blanchon
  • 13,364
  • 4
  • 73
  • 81
  • It's best to change the locale in the [runner](https://stackoverflow.com/a/45894829/52499). – x-yuri Jan 25 '21 at 19:50
  • Indeed, now that you altered the answer, I realize that you can apply the context manager to all tests. However, I still think my solution is simpler, and as we all know, "Simple is better than complex." – Benoit Blanchon Jan 26 '21 at 09:20
  • You're apparently using the private part of the interface, one day it may break. Like it did in [another case](https://stackoverflow.com/a/55549083/52499). I had to update the answer, since it stopped working with recent `factory-boy`. – x-yuri Jan 26 '21 at 11:00
0

Further to @xelnor's answer, if using pytest (instead of Django manage.py test), add a hookwrapper on the pytest_runtestloop hook in your conftest.py to set the default locale for all the tests:

@pytest.hookimpl(hookwrapper=True)
def pytest_runtestloop(session):
    with factory.Faker.override_default_locale(translation.to_locale(settings.LANGUAGE_CODE)):
        outcome = yield
amolbk
  • 797
  • 1
  • 11
  • 23