Change default faker locale in factory_boy
Asked Answered
K

5

12

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'
Kristalkristan answered 19/8, 2017 at 16:40 Comment(0)
U
10

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())
Unpleasantness answered 6/4, 2019 at 11:40 Comment(2)
Faker 5.6.5 (and possibly all versions > 4.17.1) doesn't have generate() but have evaluate(None, None, {"locale": None}).Hurrah
@Hurrah generate() belonged to factory-boy, not faker, but since factory-boy==3.1.0 the interface changed. See my updated answer.Unpleasantness
B
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.

Bali answered 26/8, 2017 at 11:26 Comment(0)
U
10

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())
Unpleasantness answered 6/4, 2019 at 11:40 Comment(2)
Faker 5.6.5 (and possibly all versions > 4.17.1) doesn't have generate() but have evaluate(None, None, {"locale": None}).Hurrah
@Hurrah generate() belonged to factory-boy, not faker, but since factory-boy==3.1.0 the interface changed. See my updated answer.Unpleasantness
J
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')
Jerkin answered 23/8, 2017 at 6:40 Comment(1)
yup, I know, but is not what I wanted to do :( devs of the project aren't too responsive this days neither :(Kristalkristan
T
3

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

import factory
factory.Faker._DEFAULT_LOCALE = 'fr_FR'
Tratner answered 10/7, 2020 at 11:36 Comment(3)
It's best to change the locale in the runner.Unpleasantness
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."Tratner
You're apparently using the private part of the interface, one day it may break. Like it did in another case. I had to update the answer, since it stopped working with recent factory-boy.Unpleasantness
J
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
Joshua answered 7/11, 2022 at 14:33 Comment(2)
from where are you importing translation?Concretion
@FranciscodeLarrañaga from django.util import translationJoshua

© 2022 - 2024 — McMap. All rights reserved.