Last active
April 8, 2023 23:59
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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