Now, we will write additional unit tests to improve the testing coverage. Specifically, we will write unit tests related to messages and users. Open the existing api/tests/test_views.py
file and insert the following lines after the last line, within the InitialTests
class. We need a new import
statement and we will declare the new PlayerTests
class. The code file for the sample is included in the restful_python_chapter_08_02
folder:
def create_message(self, message, duration, category): url = url_for('api.messagelistresource', _external=True) data = {'message': message, 'duration': duration, 'category': category} response = self.test_client.post( url, headers=self.get_authentication_headers(self.test_user_name, self.test_user_password), data=json.dumps(data)) return response def test_create_and_retrieve_message(self): """ Ensure we can create a new message and then retrieve it """ create_user_response = self.create_user(self.test_user_name, self.test_user_password) self.assertEqual(create_user_response.status_code, status.HTTP_201_CREATED) new_message_message = 'Welcome to the IoT world' new_message_category = 'Information' post_response = self.create_message(new_message_message, 15, new_message_category) self.assertEqual(post_response.status_code, status.HTTP_201_CREATED) self.assertEqual(Message.query.count(), 1) # The message should have created a new catagory self.assertEqual(Category.query.count(), 1) post_response_data = json.loads(post_response.get_data(as_text=True)) self.assertEqual(post_response_data['message'], new_message_message) new_message_url = post_response_data['url'] get_response = self.test_client.get( new_message_url, headers=self.get_authentication_headers(self.test_user_name, self.test_user_password)) get_response_data = json.loads(get_response.get_data(as_text=True)) self.assertEqual(get_response.status_code, status.HTTP_200_OK) self.assertEqual(get_response_data['message'], new_message_message) self.assertEqual(get_response_data['category']['name'], new_message_category) def test_create_duplicated_message(self): """ Ensure we cannot create a duplicated Message """ create_user_response = self.create_user(self.test_user_name, self.test_user_password) self.assertEqual(create_user_response.status_code, status.HTTP_201_CREATED) new_message_message = 'Welcome to the IoT world' new_message_category = 'Information' post_response = self.create_message(new_message_message, 15, new_message_category) self.assertEqual(post_response.status_code, status.HTTP_201_CREATED) self.assertEqual(Message.query.count(), 1) post_response_data = json.loads(post_response.get_data(as_text=True)) self.assertEqual(post_response_data['message'], new_message_message) new_message_url = post_response_data['url'] get_response = self.test_client.get( new_message_url, headers=self.get_authentication_headers(self.test_user_name, self.test_user_password)) get_response_data = json.loads(get_response.get_data(as_text=True)) self.assertEqual(get_response.status_code, status.HTTP_200_OK) self.assertEqual(get_response_data['message'], new_message_message) self.assertEqual(get_response_data['category']['name'], new_message_category) second_post_response = self.create_message(new_message_message, 15, new_message_category) self.assertEqual(second_post_response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(Message.query.count(), 1)
The preceding code adds many methods to the InitialTests
class. The create_message
method receives the desired message
, duration
, and category
(category name) for the new message as arguments. The method builds the URL and the data dictionary to compose and send an HTTP POST
method, create a new message, and return the response generated by this request. Many test methods will call the create_message
method to create a message and then compose and send other HTTP requests to the API.
The class declares the following methods whose names start with the test_
prefix:
test_create_and_retrieve_message
: Tests whether we can create a new Message
and then retrieve it.test_create_duplicated_message
: Tests whether the unique constraints don't make it possible for us to create two messages with the same message. The second time we compose and send an HTTP POST
request with a duplicate message, we must receive an HTTP 400 Bad Request
status code (status.HTTP_400_BAD_REQUEST
) and the total number of Message
objects retrieved from the database must be 1
.Open the existing api/tests/test_views.py
file and insert the following lines after the last line, within the InitialTests
class. The code file for the sample is included in the restful_python_chapter_08_02
folder:
def test_retrieve_messages_list(self):
"""
Ensure we can retrieve the messages paginated list
"""
create_user_response = self.create_user(self.test_user_name,
self.test_user_password)
self.assertEqual(create_user_response.status_code,
status.HTTP_201_CREATED)
new_message_message_1 = 'Welcome to the IoT world'
new_message_category_1 = 'Information'
post_response = self.create_message(new_message_message_1, 15,
new_message_category_1)
self.assertEqual(post_response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Message.query.count(), 1)
new_message_message_2 = 'Initialization of the board failed'
new_message_category_2 = 'Error'
post_response = self.create_message(new_message_message_2, 10,
new_message_category_2)
self.assertEqual(post_response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Message.query.count(), 2)
get_first_page_url = url_for('api.messagelistresource', _external=True)
get_first_page_response = self.test_client.get(
get_first_page_url,
headers=self.get_authentication_headers(self.test_user_name,
self.test_user_password))
get_first_page_response_data =
json.loads(get_first_page_response.get_data(as_text=True))
self.assertEqual(get_first_page_response.status_code, status.HTTP_200_OK)
self.assertEqual(get_first_page_response_data['count'], 2)
self.assertIsNone(get_first_page_response_data['previous'])
self.assertIsNone(get_first_page_response_data['next'])
self.assertIsNotNone(get_first_page_response_data['results'])
self.assertEqual(len(get_first_page_response_data['results']), 2)
self.assertEqual(get_first_page_response_data['results'][0]['message'],
new_message_message_1)
self.assertEqual(get_first_page_response_data['results'][1]['message'],
new_message_message_2)
get_second_page_url = url_for('api.messagelistresource', page=2)
get_second_page_response = self.test_client.get(
get_second_page_url,
headers=self.get_authentication_headers(self.test_user_name,
self.test_user_password))
get_second_page_response_data =
json.loads(get_second_page_response.get_data(as_text=True))
self.assertEqual(get_second_page_response.status_code,
status.HTTP_200_OK)
self.assertIsNotNone(get_second_page_response_data['previous'])
self.assertEqual(get_second_page_response_data['previous'],
url_for('api.messagelistresource', page=1))
self.assertIsNone(get_second_page_response_data['next'])
self.assertIsNotNone(get_second_page_response_data['results'])
self.assertEqual(len(get_second_page_response_data['results']), 0)
The previous code added a test_retrieve_messages_list
method to the InitialTests
class. This method tests whether we can retrieve the paginated messages list. First, the method creates two messages and then it makes sure that the retrieved list includes the two created messages in the first page. In addition, the method makes sure that the second page doesn't include any message and that the value for the previous page includes the URL for the first page.
Open the existing api/tests/test_views.py
file and insert the following lines after the last line, within the InitialTests
class. The code file for the sample is included in the restful_python_chapter_08_02
folder:
def test_update_message(self): """ Ensure we can update a single field for an existing message """ create_user_response = self.create_user(self.test_user_name, self.test_user_password) self.assertEqual(create_user_response.status_code, status.HTTP_201_CREATED) new_message_message_1 = 'Welcome to the IoT world' new_message_category_1 = 'Information' post_response = self.create_message(new_message_message_1, 30, new_message_category_1) self.assertEqual(post_response.status_code, status.HTTP_201_CREATED) self.assertEqual(Message.query.count(), 1) post_response_data = json.loads(post_response.get_data(as_text=True)) new_message_url = post_response_data['url'] new_printed_times = 1 new_printed_once = True data = {'printed_times': new_printed_times, 'printed_once': new_printed_once} patch_response = self.test_client.patch( new_message_url, headers=self.get_authentication_headers(self.test_user_name, self.test_user_password), data=json.dumps(data)) self.assertEqual(patch_response.status_code, status.HTTP_200_OK) get_response = self.test_client.get( new_message_url, headers=self.get_authentication_headers(self.test_user_name, self.test_user_password)) get_response_data = json.loads(get_response.get_data(as_text=True)) self.assertEqual(get_response.status_code, status.HTTP_200_OK) self.assertEqual(get_response_data['printed_times'], new_printed_times) self.assertEqual(get_response_data['printed_once'], new_printed_once) def test_create_and_retrieve_user(self): """ Ensure we can create a new User and then retrieve it """ new_user_name = self.test_user_name new_user_password = self.test_user_password post_response = self.create_user(new_user_name, new_user_password) self.assertEqual(post_response.status_code, status.HTTP_201_CREATED) self.assertEqual(User.query.count(), 1) post_response_data = json.loads(post_response.get_data(as_text=True)) self.assertEqual(post_response_data['name'], new_user_name) new_user_url = post_response_data['url'] get_response = self.test_client.get( new_user_url, headers=self.get_authentication_headers(self.test_user_name, self.test_user_password)) get_response_data = json.loads(get_response.get_data(as_text=True)) self.assertEqual(get_response.status_code, status.HTTP_200_OK) self.assertEqual(get_response_data['name'], new_user_name)
InitialTests
class-test_update_message
-tests whether we can update more than one fields for a message, specifically, the values for the printed_times
and printed_once
fields. The code makes sure that both fields have been updated.test_create_and_retrieve_user
: Tests whether we can create a new User
and then retrieve it.We just coded a few tests related to messages and one test related to users in order to improve test coverage and notice the impact on the test coverage report.
Now, run the following command within the same virtual environment we have been using:
nose2 -v --with-coverage
The following lines show the sample output:
test_create_and_retrieve_category (test_views.InitialTests) ... ok test_create_and_retrieve_message (test_views.InitialTests) ... ok test_create_and_retrieve_user (test_views.InitialTests) ... ok test_create_duplicated_category (test_views.InitialTests) ... ok test_create_duplicated_message (test_views.InitialTests) ... ok test_request_without_authentication (test_views.InitialTests) ... ok test_retrieve_categories_list (test_views.InitialTests) ... ok test_retrieve_messages_list (test_views.InitialTests) ... ok test_update_category (test_views.InitialTests) ... ok test_update_message (test_views.InitialTests) ... ok ------------------------------------------------------------------ Ran 10 tests in 25.938s OK ----------- coverage: platform win32, python 3.5.2-final-0 ------- Name Stmts Miss Cover ----------------------------------------- app.py 9 0 100% config.py 11 11 0% helpers.py 23 1 96% migrate.py 9 9 0% models.py 101 11 89% run.py 4 4 0% status.py 56 5 91% test_config.py 16 0 100% tests est_views.py 203 0 100% views.py 204 66 68% ----------------------------------------- TOTAL 636 107 83%
The output provided details indicating that the test runner executed 10
tests and all of them passed. The test code coverage measurement report provided by the coverage
package increased the Cover
percentage of the views.py
module from 47%
in the previous run to 68%
. In addition, the percentage of the helpers.py
module increased from 22%
to 96%
because we wrote tests that used pagination. The new additional tests we wrote executed additional code in different modules, and therefore, there is an impact in the coverage report.
18.223.237.29