Testing threaded code in Django - django

I'm testing the Django views - a view, that contains a class in itself. Can anyone help me to solve this problem: how I can to mock class or another way to test this view, because without mocking I get the error: OperationalError: no such table: - as I understand the test database is empty in new thread(I use sqlite3 in-memory for testing), but I don't know how to solve this problem.
View:
def view(self):
import threading
class Fun(threading.Thread):
def run(self):
SomeModel.class_method() - this method remove and create SomeModel instances
Fun().start()
return render_to_response('some_html.html')

Related

Is it considered good practice using too many factories in pytest?

I am trying to write tests for Django/DjangoREST project. I have decided to use pytest. I have poor experience in writing tests for Django projects specifically. So I am confused now.
Here is an example:
#pytest.mark.django_db
def test_some_view(
api_client,
simple_user,
model1_factory,
model2_factory,
... # many other model factories
modelN_factory
):
# ...
# creating here other objects that really depends on each other
# ...
model2_obj = ... # model2 object on its own side depends on model3, model4... and so on
model1_objs = []
for i in range(10):
model1_objs.append(model1_factory(some_field=100, some_model2_rel=model2_obj)
assert len(model1_objs) == 1, "Created items with duplicate `some_field`"
As you can see I have too many factories to be used in one test. But looking
at my model structure right now, I can't think of a better way. Is it ok to use so many
factories for one test? Or should I find some issues related to my tables' relations?
Any help is appreciated. Thanks in advance
The main goal of factory_boy is getting rid of fixtures; its typical use case is:
Design your Factory classes, which are basically recipes for getting a "realistic" object instance
In your test, call only the factories you need, specifying just the parameters for that test case.
As I understand it, pytest fixtures are intended for "setting up the test environment": booting the database, mocking an external service, etc.; creating objects inside the database isn't a good fit for them.
The way I'd write your code would be the following:
# factories.py
import factory
import factory.fuzzy
from . import models
class DivisionFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Division
name = factory.Faker('company')
class EmployeeFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Employee
username = factory.Faker('username')
name = factory.Faker('name')
employee_id = factory.Sequence(lambda n: 'EM199%06d' % n)
division = factory.SubFactory(DivisionFactory)
role = factory.fuzzy.FuzzyChoice(models.Employee.ROLES)
hired_on = factory.fuzzy.FuzzyDate(
start_date=datetime.date.today() - datetime.timedelta(days=100),
end_date=datetime.date.today() - datetime.timedelta(days=10),
)
We have a factory for an employee, and one for a division - and each employee gets assigned to a division.
Every mandatory field is provided; if we need to make specific factories for some object profiles, this can be added through either subclassing or using traits.
We can now write our tests, passing only the details required for the test:
# tests.py
#pytest.mark.django_db
def test_get_new_hire(api_client):
employee = factories.EmployeeFactory(
hired_on=datetime.date.today(),
division__name="Finance",
)
data = api_client.get(f'/employees/{employee.username}')
assert data['division'] == "Finance"
assert data['orientation_status'] == 'pending'
As a side note, wouldn't it make more sense to use Django's test runner directly? It's more finely tuned for Django internals: each test can be natively wrapped in a sub-transaction for performance, the test client provides helpers for in-depth introspection of view results, etc.

Same hypothesis test for different django models

I want to use hypothesis to test a tool we've written to create avro schema from Django models. Writing tests for a single model is simple enough using the django extra:
from avro.io import AvroTypeException
from hypothesis import given
from hypothesis.extra.django.models import models as hypothetical
from my_code import models
#given(hypothetical(models.Foo))
def test_amodel_schema(self, amodel):
"""Test a model through avro_utils.AvroSchema"""
# Get the already-created schema for the current model:
schema = (s for m, s in SCHEMA if m == amodel.model_name)
for schemata in schema:
error = None
try:
schemata.add_django_object(amodel)
except AvroTypeException as error:
pass
assert error is None
...but if I were to write tests for every model that can be avro-schema-ified they would be exactly the same except for the argument to the given decorator. I can get all the models I'm interested in testing with ContentTypeCache.list_models() that returns a dictionary of schema_name: model (yes, I know, it's not a list). But how can I generate code like
for schema_name, model in ContentTypeCache.list_models().items():
#given(hypothetical(model))
def test_this_schema(self, amodel):
# Same logic as above
I've considered basically dynamically generating each test method and directly attaching it to globals, but that sounds awfully hard to understand later. How can I write the same basic parameter tests for different django models with the least confusing dynamic programming possible?
You could write it as a single test using one_of:
import hypothesis.strategies as st
#given(one_of([hypothetical(model) for model in ContentTypeCache.list_models().values()]))
def test_this_schema(self, amodel):
# Same logic as above
You might want to up the number of tests run in this case using something like #settings(max_examples=settings.default.max_examples * len(ContentTypeCache.list_models())) so that it runs the same number of examples as N tests.
I would usually solve this kind of problem by parametrising the test, and drawing from the strategy internally:
#pytest.mark.parametrize('model_type', list(ContentTypeCache.list_models().values()))
#given(data=st.data())
def test_amodel_schema(self, model_type, data):
amodel = data.draw(hypothetical(model_type))
...

Django save a model instance when testing but can't find it in real database

I'am new to Django, when I was doing testing, I created a new test model record in setup() function for the following test, it succeeded, and I can access the record in the following code, but when I go to check the real database, the new record is not exist.
the setup code like this:
class UserTest(TestCase):
def setUp(self):
test_user = User.object.create(name='test')
when I set the breakpoint in the setUp() function, and after the 'test_user' is created, I can use 'User.object.get()' to obtain the created user, but when I go to real database and check the User table, I can't find this record. By the way, I use postgreSQL.
The test runner is using this:
class NoDBTestRunner(DiscoverRunner):
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
I want to know where the created data record is stored?
Any suggestion will be very helpful
Django Unit Tests create a separate database for testing. The main database is not used to avoid errors that may result from testing.

Model instance fixtures not persisted on the database

I have a test class with two methods, and want to share a saved model instance between both methods.
My fixtures:
#pytest.fixture(scope='class')
def model_factory():
class ModelFactory(object):
def get(self):
x = Model(email='test#example.org',
name='test')
x.save()
return x
return ModelFactory()
#pytest.fixture(scope='class')
def model(model_factory):
m = model_factory.get()
return m
My expectation is to receive only the model fixture on (both) my test methods and have it be the same, persisted on the database:
#pytest.mark.django_db
class TestModel(object):
def test1(self, model):
assert model.pk is not None
Model.objects.get(pk=model.pk) # Works, instance is in the db
def test2(self, model):
assert model.pk is not None # model.pk is the same as in test1
Model.objects.get(pk=model.pk) # Fails:
# *** DoesNotExist: Model matching query does not exist
I've verified using --pdb that at the end of test1, running Model.objects.all() returns the single instance I created. Meanwhile, psql shows no record:
test_db=# select * from model_table;
id | ยทยทยท fields
(0 rows)
Running the Model.objects.all() in pdb at the end of test2 returns an empty list, which is presumably right considering that the table is empty.
Why isn't my model being persisted, while the query still returns an instance anyway?
Why isn't the instance returned by the query in the second test, if my model fixture is marked scope='class' and saved? (This was my original question until I found out saving the model didn't do anything on the database)
Using django 1.6.1, pytest-django 2.9.1, pytest 2.8.5
Thanks
Tests must be independent of each other. To ensure this, Django - like most frameworks - clears the db after each test. See the documentation.
By looking at the postgres log I've found that pytest-django by default does a ROLLBACK after each test to keep things clean (which makes sense, as tests shouldn't depend on state possibly modified by earlier tests).
By decorating the test class with django_db(transaction=True) I could indeed see the data commited at the end of each test from psql, which answers my first question.
Same as before, the test runner ensures no state is kept between tests, which is the answer to my second point.
Scope argument is in this case a bit misleading, however if you would write your code like this:
#pytest.fixture(scope='class')
def model_factory(db, request):
# body
then you would get an error basically saying that database fixture has to be implemented with 'function' scope.
I would like to add that this is being currently worked on and might be an killing feature in the future ;) github pull request

Multiple Databases in Django 1.0.2 with custom manager

I asked this in the users group with no response so i thought I would try here.
I am trying to setup a custom manager to connect to another database
on the same server as my default mysql connection. I have tried
following the examples here and here but have had no luck. I get an empty tuple when returning
MyCustomModel.objects.all().
Here is what I have in manager.py
from django.db import models
from django.db.backends.mysql.base import DatabaseWrapper
from django.conf import settings
class CustomManager(models.Manager):
"""
This Manager lets you set the DATABASE_NAME on a per-model basis.
"""
def __init__(self, database_name, *args, **kwargs):
models.Manager.__init__(self, *args, **kwargs)
self.database_name = database_name
def get_query_set(self):
qs = models.Manager.get_query_set(self)
qs.query.connection = self.get_db_wrapper()
return qs
def get_db_wrapper(self):
# Monkeypatch the settings file. This is not thread-safe!
old_db_name = settings.DATABASE_NAME
settings.DATABASE_NAME = self.database_name
wrapper = DatabaseWrapper()
wrapper._cursor(settings)
settings.DATABASE_NAME = old_db_name
return wrapper
and here is what I have in models.py:
from django.db import models
from myproject.myapp.manager import CustomManager
class MyCustomModel(models.Model):
field1 = models.CharField(max_length=765)
attribute = models.CharField(max_length=765)
objects = CustomManager('custom_database_name')
class Meta:
abstract = True
But if I run MyCustomModel.objects.all() I get an empty list.
I am pretty new at this stuff so I am not sure if this works with
1.0.2, I am going to look into the Manager code to see if I can figure
it out but I am just wondering if I am doing something wrong here.
UPDATE:
This now in Django trunk and will be part of the 1.2 release
http://docs.djangoproject.com/en/dev/topics/db/multi-db/
You may want to speak to Alex Gaynor as he is adding MultiDB support and its pegged for possible release in Django 1.2. I'm sure he would appreciate feedback and input from those that are going to be using MultiDB. There is discussions about it in the django-developers mainling list. His MultiDB branch may even be useable, I'm not sure.
Since I guess you probably can't wait and if the MultiDB branch isn't usable, here are your options.
Follow Eric Flows method, bearing in mind that its not supported and new released of Django may break it. Also, some comments suggest its already been broken. This is going to be hacky.
Your other option would be to use a totally different database access method for one of your databases. Perhaps SQLAlchemy for one and then Django ORM. I'm going by the guess that one is likely to be more Django centric and the other is a legacy database.
To summarise. I think hacking MultiDB into Django is probably the wrong way to go unless your prepared to keep up with maintaining your hacks later on. Therefore I think another ORM or database access would give you the cleanest route as then you are not going out with supported features and at the end of the day, its all just Python.
My company has had success using multiple databases by closely following this blog post: http://www.eflorenzano.com/blog/post/easy-multi-database-support-django/
This probably isnt the answer your looking for, but its probably best if you move everything you need into the one database.