Skip to content

Instantly share code, notes, and snippets.

@sveetch
Last active April 8, 2023 23:59
Show Gist options
  • Save sveetch/48667706375eafbc607ea905743eb23b to your computer and use it in GitHub Desktop.
Save sveetch/48667706375eafbc607ea905743eb23b to your computer and use it in GitHub Desktop.
A demonstration of basic view, form and field tests with Pytest and django-pytest. For easiness, everything is in the test file except the template which you will need to put into your template directory.
<form action="/sample/" method="post">
{% csrf_token %}
{% if form.non_field_errors %}{% spaceless %}
<div class="non_field_errors">{{ form.non_field_errors }}</div>
{% endspaceless %}{% endif %}
{% for field in form %}
<div class="{{ field.html_name }}__container">
{% for error in field.errors %}
<p class="{{ field.html_name }}__error">{{ error }}</p>
{% endfor %}
{{ field.label_tag }}
{{ field }}
{% if field.help_text %}
<p class="{{ field.html_name }}__help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<input type="submit" value="Submit">
</form>
"""
The form part
"""
from django import forms
from django.utils.translation import gettext_lazy as _
CHOICES = (
("foo", _("Foo")),
("bar", _("Bar")),
)
class SampleForm(forms.Form):
name = forms.CharField(
required=True,
)
features = forms.MultipleChoiceField(
choices=CHOICES,
required=False,
)
def get_payload(self):
"""
Dummy method just for demonstration
"""
return {
"success": True,
}
"""
The view part
"""
from django.views.generic.edit import FormView
from django.http import JsonResponse
class SampleFormView(FormView):
"""
A very basic form view
"""
template_name = "test_view_form.html"
form_class = SampleForm
success_url = None
def form_valid(self, form):
# Easier to check than redirection and can carry some infos
return JsonResponse(
form.get_payload()
)
"""
The test part
"""
import json
import pytest
from django.core.exceptions import ValidationError
def test_feature_field():
"""
A field can be directly tested to check its validation for given values.
"""
field = forms.MultipleChoiceField(
choices=CHOICES,
required=True,
)
# Valid
assert field.clean(["foo"]) == ["foo"]
# Invalid empty value on required field
with pytest.raises(ValidationError) as exc_info:
field.clean("")
assert exc_info.value.args[0] == "This field is required."
# Invalid value type
with pytest.raises(ValidationError) as exc_info:
field.clean("foo")
assert exc_info.value.args[0] == "Enter a list of values."
def test_form_initial(db, rf):
"""
View with form in initial state should correctly response and output the form
with expected fields.
The hint:
Build a dummy request object (here with pytest fixture "rf") and setup the view.
It's a bit different than common view mechanic so you need to trigger the template
rendering (if any) to get HTML output.
"""
request = rf.get("/")
view = SampleFormView()
view.setup(request)
# The view context you can check
context = view.get_context_data()
# Get response object
response = view.dispatch(request)
assert response.status_code == 200
# Out of the real request/view/response process, we need to force template
# rendering, this is the way
rendered = response.render()
# Need to decode content from unicode
html = rendered.content.decode()
# Then you can check anything from rendered HTML
expected = '<select name="features" id="id_features" multiple>'
assert (expected in html) is True
def test_form_post_empty(db, rf):
"""
View with form receiving a POST request without required field values will
display error.
"""
request = rf.post("/", {})
view = SampleFormView()
view.setup(request)
response = view.dispatch(request)
assert response.status_code == 200
rendered = response.render()
html = rendered.content.decode()
expected = '<p class="name__error">This field is required.</p>'
assert (expected in html) is True
def test_form_post_invalid(db, rf):
"""
View with form receiving a POST request with invalid field value should display
field error.
"""
request = rf.post("/", {
"name": "Hello world!",
"features": "wrong",
})
view = SampleFormView()
view.setup(request)
response = view.dispatch(request)
assert response.status_code == 200
rendered = response.render()
html = rendered.content.decode()
expected = (
'<p class="features__error">Select a valid choice. wrong is not one of the '
'available choices.</p>'
)
assert (expected in html) is True
def test_form_post_success(db, rf):
"""
View with form receiving a POST request with valid field values should execute the
``is_valid`` method from view and return its response.
"""
request = rf.post("/", {
"name": "Hello world!",
"features": ["foo"],
})
view = SampleFormView()
view.setup(request)
response = view.dispatch(request)
assert response.status_code == 200
assert response.headers["Content-Type"] == "application/json"
assert json.loads(response.content) == {
"success": True,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment