Writing tests for
the API call threw up a few basic errors that I had overlooked along with the
fact that I had forgotten to add the confirm password field. The confirm
password field will come next, but first the tests and the changes in the code
to make the tests pass.
To begin with the most
basic test for the API:
from rest_framework.test import APIClient def test_register_new_user(): '''Test API to register new user''' client = APIClient() # Should result in a user created in db api_response = client.post( '/user/register-user', { 'username': 'someuser@domain.com', 'password': 'somepass' }, format='json' ) assert api_response.status_code == 201 assert api_response.data['username'] == 'someuser@domain.com' assert hasattr(api_response.data, 'password') == False assert api_response.data['is_active'] == False users_in_db = User.objects.all().count() assert users_in_db == 1
However, even
something as this fails. Examining the api_response.data shows Bad request. The
reason is that I have been using request.POST for populating the
UserSerializer. Since I am using Request from DRF, I do not need to use
request.POST as that still contains the encodings with a submitted form.
Instead I can use request.data which contains a dictionary with the fields.
This way, the API extracts the username and password whether a form was filled
with the link accessed in a browser or the data submitted through Postman or in
this case the test.
The tests for the API can be expanded to a few other
cases:
# Should fail model validation api_response = client.post( '/user/register-user', { 'username': 'someuser', 'password': 'somepass' }, format='json' ) assert api_response.status_code == 400 # Should fail because of missing password field api_response = client.post( '/user/register-user', { 'username': 'someuser1@domain.com', }, format='json' ) assert api_response.status_code == 400 # Should fail because of missing username field api_response = client.post( '/user/register-user', { 'password': 'somepass', }, format='json' ) assert api_response.status_code == 400 # Should fail become of missing username and password api_response = client.post( '/user/register-user', format='json' ) assert api_response.status_code == 400 # Should fail because user has been created above api_response = client.post( '/user/register-user', { 'username': 'someuser@domain.com', 'password': 'somepass' }, format='json' ) assert api_response.status_code == 400 users_in_db = User.objects.all().count() assert users_in_db == 1
In the case of certain tests such as when an API
request is submitted without a password field, an error is returned that this
field is required. However, the error message merely is “This field is required”
as the error returned is from the UserSerializer validator for the password
field. To make it clear that the field is missing, the error response returned
in the view has to be changed to:
error_list = [ '{error_field}-{error_text}'.format( error_field=e, error_text=user.errors[e][0].title() ) for e in user.errors ]
This formatting of the error string will make it
clear with the field before the error message. Something that is interesting in
the errors in the API responses is the error when the test attempts to create a
duplicate user. The error is generated from the User model that is in-built
with Django. However, examining the error message gives “username-A User With
That Username Already Exists.” This shows that the error is generated by the
serializer rather than the model. How does the error from the model get raised
to the level of the Serializer that is created from the model? This is
something that is enforced at the level of the database as the username is a
unique field.
The next change that needs to be made is that a confirm_password field needs to be added when registering a user.
No comments:
Post a Comment