Improving testing coverage

Now, we will write additional unit tests to improve the testing coverage. Specifically, we will write unit tests related to the hexacopter motor and the altimeter. Open the existing test_hexacopter.py file and insert the following lines after the last line. The code file for the sample is included in the restful_python_chapter_10_03 folder:

    def test_set_and_get_hexacopter_motor_speed(self): 
        """ 
        Ensure we can set and get the hexacopter's motor speed 
        """ 
        patch_args = {'motor_speed': 700} 
        patch_response = self.fetch( 
            '/hexacopters/1',  
            method='PATCH',  
            body=json.dumps(patch_args)) 
        self.assertEqual(patch_response.code, status.HTTP_200_OK) 
        get_response = self.fetch( 
            '/hexacopters/1', 
            method='GET') 
        self.assertEqual(get_response.code, status.HTTP_200_OK) 
        get_response_data = escape.json_decode(get_response.body) 
        self.assertTrue('speed' in get_response_data.keys()) 
        self.assertTrue('turned_on' in get_response_data.keys()) 
        self.assertEqual(get_response_data['speed'],  
                         patch_args['motor_speed']) 
        self.assertEqual(get_response_data['turned_on'],  
                         True) 
 
    def test_get_altimeter_altitude(self): 
        """ 
        Ensure we can get the altimeter's altitude 
        """ 
        get_response = self.fetch( 
            '/altimeters/1', 
            method='GET') 
        self.assertEqual(get_response.code, status.HTTP_200_OK) 
        get_response_data = escape.json_decode(get_response.body) 
        self.assertTrue('altitude' in get_response_data.keys()) 
        self.assertGreaterEqual(get_response_data['altitude'],  
                         0) 
        self.assertLessEqual(get_response_data['altitude'],  
                         3000) 

The previous code added the following two methods to the TestHexacopter class whose names start with the test_ prefix:

  • test_set_and_get_hexacopter_motor_speed: This tests whether we can set and get the hexacopter's motor speed.
  • test_get_altimeter_altitude: This tests whether we can retrieve the altitude value from the altimeter.

We just coded a few tests related to the hexacopter and the altimeter 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. Notice that the numbers shown in the report might have small differences if our code includes additional lines or comments:

test_get_altimeter_altitude (test_hexacopter.TestHexacopter) ... 
I've started retrieving the altitude
I've finished retrieving the altitude
ok
test_set_and_get_hexacopter_motor_speed (test_hexacopter.TestHexacopter) ... I've started setting the hexacopter's motor speed
I've finished setting the hexacopter's motor speed
I've started retrieving hexacopter's status
I've finished retrieving hexacopter's status
ok
test_set_and_get_led_brightness_level (test_hexacopter.TestHexacopter) ... I've started setting the Blue LED's brightness level
I've finished setting the Blue LED's brightness level
I've started setting the White LED's brightness level
I've finished setting the White LED's brightness level
I've started retrieving Blue LED's status
I've finished retrieving Blue LED's status
I've started retrieving White LED's status
I've finished retrieving White LED's status
ok
--------------------------------------------------------------
Ran 3 tests in 2.282s
OK
----------- coverage: platform win32, python 3.5.2-final-0 ---
Name           Stmts   Miss  Cover
----------------------------------
async_api.py     129     38    71%
drone.py          57      4    93%
----------------------------------
TOTAL            186     42    77%

The output provided details indicating that the test runner executed 3 tests and all of them passed. The test code coverage measurement report provided by the coverage package increased the Cover percentage of the async_api.py module from 47% in the previous run to 71%. In addition, the percentage of the drone.py module increased from 68% to 93% because we wrote tests that worked with all the components for the drone. The new additional tests we wrote executed additional code in the two modules, and therefore, there is an impact in the coverage report.

If we take a look at the missing statements, we will notice that we aren't testing scenarios where validations fail. Now, we will write additional unit tests to improve the testing coverage further. Specifically, we will write unit tests to make sure that we cannot set invalid brightness levels for the LEDs, we cannot set invalid motor speeds for the hexacopter, and we receive an HTTP 404 Not Found status code when we try to access a resource that doesn't exist. Open the existing test_hexacopter.py file and insert the following lines after the last line. The code file for the sample is included in the restful_python_chapter_10_04 folder:

    def test_set_invalid_brightness_level(self): 
        """ 
        Ensure we cannot set an invalid brightness level for a LED 
        """ 
        patch_args_led_1 = {'brightness_level': 256} 
        patch_response_led_1 = self.fetch( 
            '/leds/1',  
            method='PATCH',  
            body=json.dumps(patch_args_led_1)) 
        self.assertEqual(patch_response_led_1.code, status.HTTP_400_BAD_REQUEST) 
        patch_args_led_2 = {'brightness_level': -256} 
        patch_response_led_2 = self.fetch( 
            '/leds/2',  
            method='PATCH',  
            body=json.dumps(patch_args_led_2)) 
        self.assertEqual(patch_response_led_2.code, status.HTTP_400_BAD_REQUEST) 
        patch_response_led_3 = self.fetch( 
            '/leds/2',  
            method='PATCH',  
            body=json.dumps({})) 
        self.assertEqual(patch_response_led_3.code, status.HTTP_400_BAD_REQUEST) 
 
    def test_set_brightness_level_invalid_led_id(self): 
        """ 
        Ensure we cannot set the brightness level for an invalid LED id 
        """ 
        patch_args_led_1 = {'brightness_level': 128} 
        patch_response_led_1 = self.fetch( 
            '/leds/100',  
            method='PATCH',  
            body=json.dumps(patch_args_led_1)) 
        self.assertEqual(patch_response_led_1.code, status.HTTP_404_NOT_FOUND) 
 
    def test_get_brightness_level_invalid_led_id(self): 
        """ 
        Ensure we cannot get the brightness level for an invalid LED id 
        """ 
        patch_response_led_1 = self.fetch( 
            '/leds/100',  
            method='GET') 
        self.assertEqual(patch_response_led_1.code, status.HTTP_404_NOT_FOUND) 
 
    def test_set_invalid_motor_speed(self): 
        """ 
        Ensure we cannot set an invalid motor speed for the hexacopter 
        """ 
        patch_args_hexacopter_1 = {'motor_speed': 89000} 
        patch_response_hexacopter_1 = self.fetch( 
            '/hexacopters/1',  
            method='PATCH',  
            body=json.dumps(patch_args_hexacopter_1)) 
        self.assertEqual(patch_response_hexacopter_1.code,
        status.HTTP_400_BAD_REQUEST) 
        patch_args_hexacopter_2 = {'motor_speed': -78600} 
        patch_response_hexacopter_2 = self.fetch( 
            '/hexacopters/1',  
            method='PATCH',  
            body=json.dumps(patch_args_hexacopter_2)) 
        self.assertEqual(patch_response_hexacopter_2.code,
        status.HTTP_400_BAD_REQUEST) 
        patch_response_hexacopter_3 = self.fetch( 
            '/hexacopters/1',  
            method='PATCH',  
            body=json.dumps({})) 
        self.assertEqual(patch_response_hexacopter_3.code,
        status.HTTP_400_BAD_REQUEST) 
 
    def test_set_motor_speed_invalid_hexacopter_id(self): 
        """ 
        Ensure we cannot set the motor speed for an invalid hexacopter id 
        """ 
        patch_args_hexacopter_1 = {'motor_speed': 128} 
        patch_response_hexacopter_1 = self.fetch( 
            '/hexacopters/100',  
            method='PATCH',  
            body=json.dumps(patch_args_hexacopter_1)) 
        self.assertEqual(patch_response_hexacopter_1.code,
        status.HTTP_404_NOT_FOUND) 
 
    def test_get_motor_speed_invalid_hexacopter_id(self): 
        """ 
        Ensure we cannot get the motor speed for an invalid hexacopter id 
        """ 
        patch_response_hexacopter_1 = self.fetch( 
            '/hexacopters/5',  
            method='GET') 
        self.assertEqual(patch_response_hexacopter_1.code,
        status.HTTP_404_NOT_FOUND) 
 
    def test_get_altimeter_altitude_invalid_altimeter_id(self): 
        """ 
        Ensure we cannot get the altimeter's altitude for an invalid altimeter id 
        """ 
        get_response = self.fetch( 
            '/altimeters/5', 
            method='GET') 
        self.assertEqual(get_response.code, status.HTTP_404_NOT_FOUND) 

The previous code added the following seven methods to the TestHexacopter class whose names start with the test_ prefix:

  • test_set_invalid_brightness_level: This makes sure that we cannot set an invalid brightness level for an LED through an HTTP PATCH request.
  • test_set_brightness_level_invalid_led_id: This makes sure that we cannot set the brightness level for an invalid LED ID through an HTTP PATCH request.
  • test_get_brightness_level_invalid_led_id: This makes sure that we cannot get the brightness level for an invalid LED ID.
  • test_set_invalid_motor_speed: This makes sure that we cannot set an invalid motor seed for the hexacopter through an HTTP PATCH request.
  • test_set_motor_speed_invalid_hexacopter_id: This makes sure that we cannot set the motor speed for an invalid hexacopter ID through an HTTP PATCH request.
  • test_get_motor_speed_invalid_hexacopter_id: This makes sure that we cannot get the motor speed for an invalid hexacopter ID.
  • test_get_altimeter_altitude_invalid_altimeter_id: This makes sure that we cannot get the altitude value for an invalid altimeter ID.

We coded many tests that will make sure that all the validations work as expected. 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. Notice that the numbers shown in the report might have small differences if our code includes additional lines or comments:

I've finished retrieving the altitude
ok
test_get_altimeter_altitude_invalid_altimeter_id (test_hexacopter.TestHexacopter) ... WARNING:tornado.access:404 GET /altimeters/5 (127.0.0.1) 1.00ms
ok
test_get_brightness_level_invalid_led_id (test_hexacopter.TestHexacopter) ... WARNING:tornado.access:404 GET /leds/100 (127.0.0.1) 2.01ms
ok
test_get_motor_speed_invalid_hexacopter_id (test_hexacopter.TestHexacopter) ... WARNING:tornado.access:404 GET /hexacopters/5 (127.0.0.1) 2.01ms
ok
test_set_and_get_hexacopter_motor_speed (test_hexacopter.TestHexacopter) ... I've started setting the hexacopter's motor speed
I've finished setting the hexacopter's motor speed
I've started retrieving hexacopter's status
I've finished retrieving hexacopter's status
ok
test_set_and_get_led_brightness_level (test_hexacopter.TestHexacopter) ... I've started setting the Blue LED's brightness level
I've finished setting the Blue LED's brightness level
I've started setting the White LED's brightness level
I've finished setting the White LED's brightness level
I've started retrieving Blue LED's status
I've finished retrieving Blue LED's status
I've started retrieving White LED's status
I've finished retrieving White LED's status
ok
test_set_brightness_level_invalid_led_id (test_hexacopter.TestHexacopter) ... WARNING:tornado.access:404 PATCH /leds/100 (127.0.0.1) 1.01ms
ok
test_set_invalid_brightness_level (test_hexacopter.TestHexacopter) ... I've started setting the Blue LED's brightness level
I've failed setting the Blue LED's brightness level
WARNING:tornado.access:400 PATCH /leds/1 (127.0.0.1) 13.51ms
I've started setting the White LED's brightness level
I've failed setting the White LED's brightness level
WARNING:tornado.access:400 PATCH /leds/2 (127.0.0.1) 10.03ms
WARNING:tornado.access:400 PATCH /leds/2 (127.0.0.1) 2.01ms
ok
test_set_invalid_motor_speed (test_hexacopter.TestHexacopter) ... I've started setting the hexacopter's motor speed
I've failed setting the hexacopter's motor speed
WARNING:tornado.access:400 PATCH /hexacopters/1 (127.0.0.1) 19.27ms
I've started setting the hexacopter's motor speed
I've failed setting the hexacopter's motor speed
WARNING:tornado.access:400 PATCH /hexacopters/1 (127.0.0.1) 9.04ms
WARNING:tornado.access:400 PATCH /hexacopters/1 (127.0.0.1) 1.00ms
ok
test_set_motor_speed_invalid_hexacopter_id (test_hexacopter.TestHexacopter) ... WARNING:tornado.access:404 PATCH /hexacopters/100 (127.0.0.1) 1.00ms
ok
----------------------------------------------------------------------
Ran 10 tests in 5.905s
OK
----------- coverage: platform win32, python 3.5.2-final-0 -----------
Name           Stmts   Miss  Cover
----------------------------------
async_api.py     129      5    96%
drone.py          57      0   100%
----------------------------------
TOTAL            186      5    97%

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 async_api.py module from 71% in the previous run to 97%. In addition, the percentage of the drone.py module increased from 93% to 100%. If we check the coverage report, we will notice that the only statements that aren't executed are the statements included in the main method for the async_api.py module because they aren't part of the tests. Thus, we can say that we have 100% coverage.

Now that we have a great test coverage, we can generate the requirements.txt file that lists the application dependencies together with their versions. This way, any platform in which we decide to deploy the RESTful API will be able to easily install all the necessary dependencies listed in the file.

Run the following pip freeze to generate the requirements.txt file:

pip freeze > requirements.txt

The following lines show the content of a sample generated requirements.txt file. However, bear in mind that many packages increase their version number quickly and you might see different versions in your configuration:

cov-core==1.15.0
coverage==4.2
nose2==0.6.5
six==1.10.0
tornado==4.4.1
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.49.247