Merge branch 'improve-factories' into tests

This commit is contained in:
2020-05-21 20:13:30 +02:00
6 changed files with 146 additions and 22 deletions

View File

@@ -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

View 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)

View File

@@ -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')

View File

@@ -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')))

View File

@@ -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)

View File

@@ -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)