Merge branch 'improve-factories' into tests
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
""" Module generating useful models in 1 place """
|
||||
from inspect import signature
|
||||
from typing import Type, TypeVar
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
@@ -11,26 +12,66 @@ T = TypeVar('T', bound=django.db.models.Model)
|
||||
|
||||
def create(model: Type[T], **kwargs) -> T:
|
||||
if model is models.Video:
|
||||
return models.Video.objects.create(**{**dict(title='Title', slug='slug', description='Description'), **kwargs})
|
||||
return _create_with_defaults(models.Video, kwargs,
|
||||
title=lambda x: 'Title {}'.format(x),
|
||||
slug=lambda x: 'slug-{}'.format(x),
|
||||
description=lambda x: 'Description {}'.format(x),
|
||||
)
|
||||
|
||||
if model is models.Transcoding:
|
||||
video = create(models.Video, title='Title', slug='slug', description='Description') \
|
||||
if 'video' not in kwargs else None
|
||||
defaults = dict(
|
||||
video=video,
|
||||
def url():
|
||||
# only URL if no upload for they are mutually exclusive
|
||||
if 'upload' not in kwargs:
|
||||
return 'https://some_url'
|
||||
|
||||
return _create_with_defaults(models.Transcoding, kwargs,
|
||||
video=lambda: create(models.Video),
|
||||
quality=models.qualities[0].name,
|
||||
type=str(models.transcoding_types[0]),
|
||||
url=url,
|
||||
)
|
||||
if 'upload' not in kwargs:
|
||||
# only URL if no upload for they are multually exclusive
|
||||
defaults['url'] = 'https://some_url'
|
||||
|
||||
return models.Transcoding.objects.create(**{**defaults, **kwargs})
|
||||
|
||||
if model is models.Upload:
|
||||
file = SimpleUploadedFile('some_file.txt', b'some contents') \
|
||||
if 'file' not in kwargs else None
|
||||
return models.Upload.objects.create(**{**dict(file=file), **kwargs})
|
||||
return _create_with_defaults(models.Upload, kwargs, file=SimpleUploadedFile('some_file.txt', b'some contents'))
|
||||
|
||||
raise NotImplementedError('Factory for %s not implemented' % model)
|
||||
|
||||
|
||||
# TODO fix annoying dict notation to something more gentle.
|
||||
def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T:
|
||||
"""
|
||||
Return created django model instance.
|
||||
When providing lambda as default item, the result of the lambda will be taken.
|
||||
The lambda will ONLY be executed when not provided in kwargs.
|
||||
|
||||
When a lambda requires an argument, the primary key of the to be created object
|
||||
will be provided to that argument. This is useful for generating unique fields.
|
||||
|
||||
:param model: django model to create
|
||||
:param kwargs: keyword arguments to fill the model
|
||||
:param defaults: default keyword arguments to use when not mentioned in kwargs
|
||||
"""
|
||||
|
||||
_next_pk = 0
|
||||
def next_pk():
|
||||
# Queries next pk only one time during creation
|
||||
nonlocal _next_pk
|
||||
if _next_pk == 0:
|
||||
_next_pk = _query_next_pk(model)
|
||||
return _next_pk
|
||||
|
||||
for k, v in defaults.items():
|
||||
if callable(v) and not k in kwargs:
|
||||
if len(signature(v).parameters) == 1:
|
||||
result = v(next_pk())
|
||||
else:
|
||||
result = v()
|
||||
defaults[k] = result
|
||||
|
||||
return model.objects.create(**{**defaults, **kwargs})
|
||||
|
||||
|
||||
def _query_next_pk(model: Type[T]) -> int:
|
||||
try:
|
||||
return model.objects.order_by('-pk').first().pk + 1
|
||||
except AttributeError:
|
||||
return 1
|
||||
|
||||
16
tests/videodinges/test_factories/test_create.py
Normal file
16
tests/videodinges/test_factories/test_create.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.db import models
|
||||
|
||||
from tests.videodinges import factories
|
||||
from django.test import TestCase
|
||||
|
||||
class CreateTestCase(TestCase):
|
||||
|
||||
def test_factory_returns_model(self):
|
||||
|
||||
class NotImplementedModel(models.Model):
|
||||
class Meta:
|
||||
app_label = 'some_test_label'
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
factories.create(NotImplementedModel)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import os
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import TestCase
|
||||
from videodinges.models import Transcoding, Video
|
||||
from tests.videodinges import factories
|
||||
from datetime import datetime
|
||||
from videodinges.models import Transcoding, Video, Upload
|
||||
from tests.videodinges import factories, UploadMixin
|
||||
|
||||
class TranscodingTestCase(TestCase):
|
||||
def test_factory_returns_model(self):
|
||||
transcoding = factories.create(Transcoding)
|
||||
self.assertEqual(transcoding.video.slug, 'slug')
|
||||
self.assertEqual(transcoding.video.slug, 'slug-1')
|
||||
self.assertEqual(transcoding.quality, '360p')
|
||||
self.assertEqual(transcoding.type, 'video/webm')
|
||||
self.assertEqual(transcoding.url, 'https://some_url')
|
||||
@@ -24,3 +26,30 @@ class TranscodingTestCase(TestCase):
|
||||
self.assertEqual(transcoding.quality, '720p')
|
||||
self.assertEqual(transcoding.type, 'video/mp4')
|
||||
self.assertEqual(transcoding.url, 'http://another_url')
|
||||
|
||||
def test_does_not_create_video_when_providing_one(self):
|
||||
transcoding = factories.create(
|
||||
Transcoding,
|
||||
quality='720p',
|
||||
type='video/mp4',
|
||||
url='http://another_url',
|
||||
video=factories.create(Video, slug='yet-another-video-slug')
|
||||
)
|
||||
|
||||
self.assertEquals(Video.objects.all().count(), 1)
|
||||
|
||||
|
||||
class TranscodingWithUploadTestCase(UploadMixin, TestCase):
|
||||
def test_can_assign_upload(self):
|
||||
transcoding = factories.create(
|
||||
Transcoding,
|
||||
quality='720p',
|
||||
type='video/mp4',
|
||||
video=factories.create(Video, slug='yet-another-video-slug'),
|
||||
upload=factories.create(Upload, file=SimpleUploadedFile('my_upload.txt', b'some_contents'))
|
||||
)
|
||||
|
||||
self.assertTrue(os.path.exists(os.path.join(self.media_root.name, 'my_upload.txt')))
|
||||
with open(os.path.join(self.media_root.name, 'my_upload.txt'), 'rb') as f:
|
||||
self.assertEquals(f.read(), b'some_contents')
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import os
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import TestCase
|
||||
|
||||
from tests.videodinges import factories, UploadMixin
|
||||
@@ -7,3 +10,10 @@ class UploadTestCase(UploadMixin, TestCase):
|
||||
def test_model_is_created(self):
|
||||
upload = factories.create(Upload)
|
||||
self.assertEqual(upload.file.name, 'some_file.txt')
|
||||
self.assertTrue(os.path.exists(os.path.join(self.media_root.name, 'some_file.txt')))
|
||||
|
||||
def test_upload_does_not_create_file_when_providing_upload(self):
|
||||
upload = factories.create(Upload, file=SimpleUploadedFile('my_file.txt', b'some contents'))
|
||||
self.assertEqual(upload.file.name, 'my_file.txt')
|
||||
self.assertFalse(os.path.exists(os.path.join(self.media_root.name, 'some_file.txt')))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.media_root.name, 'my_file.txt')))
|
||||
|
||||
@@ -6,8 +6,36 @@ from datetime import datetime
|
||||
class VideoTestCase(TestCase):
|
||||
def test_factory_returns_model(self):
|
||||
video = factories.create(Video)
|
||||
self.assertEqual(video.slug, 'slug')
|
||||
self.assertEqual(video.title, 'Title')
|
||||
self.assertEqual(video.description, 'Description')
|
||||
self.assertEqual(video.slug, 'slug-1')
|
||||
self.assertEqual(video.title, 'Title 1')
|
||||
self.assertEqual(video.description, 'Description 1')
|
||||
self.assertIsInstance(video.created_at, datetime)
|
||||
self.assertIsInstance(video.updated_at, datetime)
|
||||
|
||||
def test_factory_can_create_multiple_models(self):
|
||||
video1 = factories.create(Video)
|
||||
video2 = factories.create(Video)
|
||||
video3 = factories.create(Video)
|
||||
|
||||
self.assertEqual(video1.slug, 'slug-1')
|
||||
self.assertEqual(video1.title, 'Title 1')
|
||||
self.assertEqual(video1.description, 'Description 1')
|
||||
self.assertIsInstance(video1.created_at, datetime)
|
||||
self.assertIsInstance(video1.updated_at, datetime)
|
||||
|
||||
self.assertEqual(video2.slug, 'slug-2')
|
||||
self.assertEqual(video2.title, 'Title 2')
|
||||
self.assertEqual(video2.description, 'Description 2')
|
||||
self.assertIsInstance(video2.created_at, datetime)
|
||||
self.assertIsInstance(video2.updated_at, datetime)
|
||||
|
||||
self.assertEqual(video3.slug, 'slug-3')
|
||||
self.assertEqual(video3.title, 'Title 3')
|
||||
self.assertEqual(video3.description, 'Description 3')
|
||||
self.assertIsInstance(video3.created_at, datetime)
|
||||
self.assertIsInstance(video3.updated_at, datetime)
|
||||
|
||||
def test_factory_runs_only_2_queries(self):
|
||||
""" Factory should only use 2 queries: one for selecting primary key, and one for inserting record """
|
||||
with self.assertNumQueries(2):
|
||||
video = factories.create(Video)
|
||||
|
||||
@@ -70,7 +70,7 @@ class VideoTestCase(UploadMixin, TestCase):
|
||||
|
||||
self.assertInHTML('<h1>Vid 1</h1>', content)
|
||||
|
||||
self.assertInHTML('<p>Description</p>', content)
|
||||
self.assertInHTML('<p>Description 1</p>', content)
|
||||
|
||||
self.assertInHTML('<strong>480p versie</strong>', content)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user