Custom faker provider for usage with factory boy and pytest - django

I am attempting to add some custom faker provider to use with factory_boy and pytest.
I put the provider in
faker_providers/foo.py/Provider.
In my factories.py file, I have to import foo.py and then register by running:
factory.Faker.add_provider(foo.Provider)
I am thinking of using pytest_sessionstart(session) to auto-register all the custom provider under faker_providers. Is there a way to do that?
Any suggestions for other ways to organize and register custom providers would also be appreciated.

Instead of instantiating a Faker to import from conftest, as of 2022, you can do the following inside your conftest file:
from factory import Faker
Faker.add_provider(CustomProvider)
And now you can just import from factory import Faker wherever you go. This works because the Faker class has a class attribute of type dictionary that saves your new providers.
The reason I put it in conftest is because this is where code is run initially for all pytest. You can also put this in a pytest plugin setup method as well, but I found this the easiest.
The other way of doing this that I implemented was to have a utils folder with all my custom providers. Then, in a providers.py, I would add all my providers:
from factory import Faker
from .custom_provider import CustomProvider
for provider in (CustomProvider,):
Faker.add_provider(provider)
Then in conftest.py, I would simply import this file:
import path.to.provider # noqa
This way, I don't clutter my conftest too much.

It seems like a design choice and only you know the best answer to it.
However, I would recommend instantiating faker = Faker() once for all tests after that adding all providers in a configuration file. And import faker from that place to everywhere it's needed.
It seems like conftest.py is a good choice for that.

Related

Importing multiple models in create_app method

I have a base flask app that I have put together to build future projects from. I have it set up with an application factory method "create_app".
https://github.com/thenetimp/flask_base_v2/blob/master/app/init.py#L1-L32
In the create_app method, I am initializing the application object. Then pass it to the previously initialized db object eventually calling db.create_all to create the database from my model(s).
In order for this to work I have to import any model I may have into the create_app function. This isn't problematic for a small database with a few tables, but if I have a database with a large number of tables it seems like there should be a better way. from app.models import * doesn't work inside functions, so I have to ask is there another way to manage this?
in the code I was trying to do
def create_app():
from app.models import *
...
when all I needed to do was
def create_app():
import app.models
...
get so use to doing it one way I forgot about the other.

Could not import 'cookbook.schema.schema'

I am going through the official tutorial provided by graphene-python for their library.
I, like a few others I have seen online, am having some serious issues trying to simply import the schema file within the project folder (project_name/schema.py). For reference, the project_name is cookbook as it is denoted within the tutorial.
This is within my settings.py:
GRAPHENE = {
'SCHEMA': 'cookbook.schema.schema'
}
and this is in the schema file tiself (project_name/schema.py):
import graphene
import cookbook.schema
class Query(cookbook.schema.Query, graphene.ObjectType):
# This class will inherit from multiple Queries
# as we begin to add more apps to our project
pass
schema = graphene.Schema(query=Query)
The error that I am getting is:
Could not import 'cookbook.schema.schema' for Graphene setting 'SCHEMA'. AttributeError: module 'cookbook' has no attribute 'schema'.
I have also tried a few other tutorials as well, but haven't had any luck. My project is on django 2.0.2 and graphene 2.0.1. Any help would be much appreciated.
Did you add this to your installed apps?
INSTALLED_APPS = [
'graphene_django',
]
the error says that cookbook has no attribute schema.
Therefore the import cookbook.schema is not working in your schema.py
the example says
import cookbook.ingredients.schema
I actually had incompatible versions of Django, Graphene and Django-environ.
To solve, I made a virtualenv using mkvirtualenv. After that, I was able to follow this tutorial without any issues. It is capable of being stood up without a virtual environment, but it was far easier to just define one and get moving with a clean slate.

Import Django models from outside the project

I'm working on an app which uses two tables from different databases.I manage to make the connection and make the tables structures in models.py, but now one I change the models.py file, I copy one of the tables in another python script, and I put the file elsewhere for other people to use it.My question it is possible in Django to import a model from outside the project? or the package?
The App is called banner_manager and in views.py I want to import a model called user from another project called django_models
when I try to import like this:
from ....models_django import models.py(in models.py it's the class "user" defined) it says: ValueError: Attempted relative import beyond top-level package
You can add this directory to PYTHONPATH for example:
export PYTHONPATH=$PYTHONPATH:/var/python/your-libs
And then just import package as normal:
import models_django

Using django-discover-runner without database

I'm trying to use django-discover-runner to test my app. It's basically a WebService frontend, so it doesn't include a database, and, apparently, django-discover-runner doesn't like that.
Looking in other questions, I've seen that with plain Django, I should inherit from DjangoTestSuiteRunner and set settings.TEST_RUNNER. It works fine. But django-discover-runner uses its own discover_runner.DiscoverRunner class, so I tried this:
from discover_runner import DiscoverRunner
class DBLessTestRunner(DiscoverRunner):
def setup_databases(self):
pass
def teardown_databases(self, *args):
pass
But it doesn't work. I get this error message:
ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.
Any idea how to get django-discover-runner working without a DataBase?
In Django 1.6 the standard Django TestCase inherits from TransactionTestCase which attempts to access the database.
To fix the problem in your test class inherit from SimpleTestCase rather then TestCase:
from django.test import SimpleTestCase
class TestViews(SimpleTestCase):
...
You should now be able to run your tests with out setting up the database.

Mocking Django Storages Model ImageField backend S3

I have a model with an ImageField that is backed by django-storages' S3Boto. I have a test the exercises the "upload image" view, but the fact that it is uploading the image to S3 is slowing down my test suite.
In the interest of speeding up my tests, what is the best practice for dealing with this issue? Should I mock out S3Boto? Perhaps there is a memory backed storage backend that works well for testing (automatic cleanup would be nice!)?
I just had this problem too. I got much faster tests by using dj-inmemorystorage.
The quick way of setting this up is by creating a test_settings.py in the same folder as your settings:
from settings import *
DEFAULT_FILE_STORAGE = 'inmemorystorage.InMemoryStorage'
...and calling ./manage.py test --settings=project.test_settings to run the tests.
My preferred way is to set up a custom test runner:
In project/test_runner.py:
from django.conf import settings
from django.test.runner import DiscoverRunner
class FastTestRunner(DiscoverRunner):
def setup_test_environment(self):
super(FastTestRunner, self).setup_test_environment()
# Don't write files
settings.DEFAULT_FILE_STORAGE = 'inmemorystorage.InMemoryStorage'
# Bonus: Use a faster password hasher. This REALLY helps.
settings.PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
)
Note: This also sets the PASSWORD_HASHER, because it significantly improves User creation time. This should NOT be set in production.
In project/settings.py:
TEST_RUNNER = 'project.test_runner.FastTestRunner'
The requirements:
pip install dj-inmemorystorage
UPDATE: changed from django-inmemorystorage to dj-inmemorystorage.
UPDATE 2: Removed django-discover-runner, as it's now the default test runner in django, and fixed the link to the PASSWORD_HASHER related blog post.
I also use S3Boto but for testing, I prefer having custom settings which include using the file system storage. You can have your custom settings declared in a file which you can then import and use in your test cases. Even so, you can mock the file storage so that the files are not actually written to disk.
Here's a sample test_settings.py
# myproject/myproject/test_settings.py
from django.test import override_settings
common_settings = override_settings(
DEFAULT_FILE_STORAGE='django.core.files.storage.FileSystemStorage',
PASSWORD_HASHERS=(
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
),
)
Usage:
from django.test import TestCase
from myproject.test_settings import common_settings
#common_settings
class MyTestCase(TestCase):
"""Tests go here"""
On mocking the file system storage you can check out my answer here on SO.
Just ran into this as well so I thought I'd put my solution up. My solution uses Mock
import mock
from django.core.files.storage import FileSystemStorage
from django.test import TestCase
class ATestCase(TestCase):
def setUp(self):
# Stuff Happens
def tearDown(self):
# more Stuff
#mock.patch('storages.backends.s3boto.S3BotoStorage', FileSystemStorage)
def test_file_stuff(self):
self.assertMagicPonies(True)
Some gotchas - make sure you have a sane MEDIA_ROOT setup in the settings. as of django 1.4, you can't use the testing context manager to override MEDIA_ROOT, so you need a separate settings config for it (https://code.djangoproject.com/ticket/17787) This was fixed in 1.6. Also, make sure your upload_to works in normal filesystem, or you will get permission errors.
I would propose to use the standard Django Storage for testing, where you can a define custom path for storage and cleanup that path in your test suite once your done. Both the storage and the path can be set in the settings and overwritten for testing.