28
loading...
This website collects cookies to deliver better user experience
index.html
in our newly created htmlscov
folder in our project:book-library > htmlscov > index.html
Book
model:class Book(models.Model):
"""Model representing a book
"""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
summary = models.TextField(
max_length=1000, help_text='Enter a brief description of the book')
isbn = models.CharField('ISBN', max_length=13, unique=True,
help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
genre = models.ManyToManyField(Genre, help_text='Select a genre for this book', related_name='books')
def __str__(self):
return self.title
def get_absolute_url(self):
"""Returns the url to access a detail record for this book.
"""
return reverse('book-detail', args=[str(self.id)])
class TestModels(TestCase):
@classmethod
def setUpTestData(self):
thriller = Genre.objects.create(name='thriller')
scifi = Genre.objects.create(name='scifi')
book = Book.objects.create(
title='test title',
summary='test summary',
isbn='test isbn',
)
book.genre.set([thriller.pk, scifi.pk])
def test_book_has_genre(self):
"""checks for genres in book
"""
book = Book.objects.first()
self.assertEqual(book.genre.count(), 2)
def test_book_str(self):
"""Checks str for book
"""
book = Book.objects.first()
self.assertEqual(str(book), "test title")
def test_book_absolute_url_with_200(self):
book = Book.objects.first()
self.assertEqual(book.get_absolute_url(), '/catalog/book/1')
response = self.client.get(book.get_absolute_url())
self.assertEqual(response.status_code, 200)
str
representation.def test_book_str(self):
"""Checks str for book
"""
book = Book.objects.first()
self.assertEqual(str(book), "test title")
test_
and it should be precise so that one can understand what the test is about. However, in cases where you feel that the naming may not be precise or meaningful make sure to use docstring to explain what it is supposed to do. def test_book_has_genre(self):
"""checks for genres in book
"""
book = Book.objects.first()
self.assertEqual(book.genre.count(), 2)
def test_book_absolute_url_with_200(self):
book = Book.objects.first()
self.assertEqual(book.get_absolute_url(), '/catalog/book/1')
response = self.client.get(book.get_absolute_url())
self.assertEqual(response.status_code, 200)
Book
model we have a method called get_absolute_url
which gets the information of a single book. We check if the status code is 200 and the get_absolute_url
method actually is the correct url.GET
and POST
is working the way it should.index
view for our testing.from .models import Book, Author, BookInstance, Genre
from django.contrib.auth.decorators import login_required
@login_required(login_url='/login/')
def index(request):
"""View function for home page of site."""
# Generate counts of some of the main objects
num_books = Book.objects.all().count()
num_instances = BookInstance.objects.all().count()
# Available books (status = 'a')
num_instances_available = BookInstance.objects.filter(status__exact='a').count()
num_authors = Author.objects.count()
# Number of visits to this view, as counted in the session variable.
num_visits = request.session.get('num_visits', 0)
request.session['num_visits'] = num_visits + 1
context = {
'num_books': num_books,
'num_instances': num_instances,
'num_instances_available': num_instances_available,
'num_authors': num_authors,
'num_visits': num_visits,
}
# Render the HTML template index.html with the data in the context variable
return render(request, 'index.html', context=context)
from django.test import TestCase
from catalog.models import Author
from django.urls import reverse
from django.contrib.auth.models import User
from model_bakery import baker
class IndexViewTest(TestCase):
@classmethod
def setUpTestData(cls):
User.objects.create_user(username='test_user', password='test_password')
books = baker.make('catalog.Book', _quantity=10)
def test_view_deny_anonymous(self):
"""Test the view for unauthenticated user if unauthenticated will redirect to login page
"""
response = self.client.get('/catalog/')
self.assertRedirects(response, '/login/?next=/catalog/')
response = self.client.post('/catalog/')
self.assertRedirects(response, '/login/?next=/catalog/')
def test_view_url_accesible_by_name(self):
"""Test view is accesible by the reverse method
"""
self.client.login(username='test_user', password='test_password')
response = self.client.get(reverse('index'))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('index'))
self.assertEqual(response.status_code, 200)
def test_view_uses_correct_template_(self):
"""Test view is using correct template
"""
self.client.login(username='test_user', password='test_password')
response = self.client.get(reverse('index'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'index.html')
def test_view_has_context_num_books(self):
self.client.login(username='test_user', password='test_password')
response = self.client.get(reverse('index'))
self.assertEqual(response.status_code, 200)
self.assertTrue('num_books' in response.context)
self.assertEqual(response.context['num_books'], 10)
def test_view_deny_anonymous(self):
"""Test the view for unauthenticated user if unauthenticated will redirect to login page
"""
response = self.client.get('/catalog/')
self.assertRedirects(response, '/login/?next=/catalog/')
response = self.client.post('/catalog/')
self.assertRedirects(response, '/login/?next=/catalog/')
def test_view_url_accessible_by_name(self):
"""Test view is accessible by the reverse method
"""
self.client.login(username='test_user', password='test_password')
response = self.client.get(reverse('index'))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('index'))
self.assertEqual(response.status_code, 200)
reverse
method is working and gets us a status code 200
for both GET
and POST
method.def test_view_uses_correct_template_(self):
"""Test view is using correct template
"""
self.client.login(username='test_user', password='test_password')
response = self.client.get(reverse('index'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'index.html')
assertTemplateUsed
method.def test_view_has_context_num_books(self):
self.client.login(username='test_user', password='test_password')
response = self.client.get(reverse('index'))
self.assertEqual(response.status_code, 200)
self.assertTrue('num_books' in response.context)
self.assertEqual(response.context['num_books'], 10)
Book
instances.from django import forms
from .models import Book
class AddBookForm(forms.ModelForm):
class Meta:
model = Book
fields = ["title"]
def clean_title(self):
title = self.cleaned_data['title']
if not title:
return title
if not title[0].isupper():
self.add_error("title", "Should start with an uppercase")
if title.endswith("."):
self.add_error("title", "Should not end with a full stop")
return title
from django.test import TestCase
from catalog.forms import AddBookForm
class AddBookFormTests(TestCase):
def test_title_starting_lowercase(self):
form = AddBookForm(data={"title": "a lowercase title"})
self.assertEqual(
form.errors["title"], ["Should start with an uppercase"]
)
def test_title_ending_full_stop(self):
form = AddBookForm(data={"title": "A stopped title."})
self.assertEqual(
form.errors["title"], ["Should not end with a full stop"]
)
def test_title_with_ampersand(self):
form = AddBookForm(data={"title": ""})
self.assertEqual(
form.errors["title"], ["This field is required."]
)
def test_title_starting_lowercase(self):
response = self.client.post(
"/books/add/", data={"title": "a lowercase title"}
)
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(
response, "Should start with an uppercase letter", html=True
)
Continuous Integration: Automatically run all tests whenever there a new commit is made. It can be done using GitHub Actions, Travis CI, Circle CI.
Mock: It is an act of replacing part of the application you are testing with a dummy version.
Pytest: It is a testing framework just like unittest
. We can also use this instead of unittest
Model Bakery: We saw a small implementation of this in our project but there is more to discuss.
TDD: Test Driven Development, this is actually quite an advance topic which takes a while to get used to, but once you start doing and understand the benefits, you will never want to go back.