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 tests.py file in the root folder for the virtual environment (Tornado01). Insert the following lines after the last line. The code file for the sample is included in the restful_python_2_11_02 folder, in the Django01/tests.py file:

async def 
test_set_and_get_hexacopter_motor_speed(http_server_client): """ Ensure we can set and get the hexacopter's motor speed """ patch_args = {'motor_speed_in_rpm': 200} patch_response = await http_server_client.fetch( '/hexacopters/1', method='PATCH', body=json.dumps(patch_args)) assert patch_response.code == HTTPStatus.OK get_response = await http_server_client.fetch( '/hexacopters/1', method='GET') assert get_response.code == HTTPStatus.OK get_response_data = escape.json_decode(get_response.body) assert 'motor_speed_in_rpm' in get_response_data.keys() assert 'is_turned_on' in get_response_data.keys() assert get_response_data['motor_speed_in_rpm'] == patch_args['motor_speed_in_rpm'] assert get_response_data['is_turned_on'] async def
test_get_altimeter_altitude_in_feet(http_server_client): """ Ensure we can get the altimeter's altitude in feet """ get_response = await http_server_client.fetch( '/altimeters/1', method='GET') assert get_response.code == HTTPStatus.OK get_response_data = escape.json_decode(get_response.body) assert 'altitude' in get_response_data.keys() assert 'unit' in get_response_data.keys() assert get_response_data['altitude'] >= 0 assert get_response_data['altitude'] <= 3000 assert get_response_data['unit'] == 'feet' async def
test_get_altimeter_altitude_in_meters(http_server_client): """ Ensure we can get the altimeter's altitude in meters """ get_response = await http_server_client.fetch( '/altimeters/1?unit=meters', method='GET') assert get_response.code == HTTPStatus.OK get_response_data = escape.json_decode(get_response.body) assert 'altitude' in get_response_data.keys() assert 'unit' in get_response_data.keys() assert get_response_data['altitude'] >= 0 assert get_response_data['altitude'] <= 914.4 assert get_response_data['unit'] == 'meters'

The previous code added the following three test functions, whose names start with the test_ prefix and who receive the http_server_client argument to use this test fixture:

  • test_set_and_get_hexacopter_motor_speed: This test function tests whether we can set and get the hexacopter's motor speed
  • test_get_altimeter_altitude_in_feet: This test function tests whether we can retrieve the altitude value from the altimeter, expressed in feet
  • test_get_altimeter_altitude_in_meters: This test function tests whether we can retrieve the altitude value from the altimeter, expressed in meters

We just coded a few tests related to the hexacopter and the altimeter in order to improve test coverage and note the impact on the test coverage report.

Now, we will use the pytest command to run tests and measure their code coverage. Make sure you run the command in the Terminal or Command Prompt window in which you have activated the virtual environment, and that you are located within its root folder (Tornado01). Run the following command:

    pytest --cov -s

The following lines show the sample output:

================================================ test session starts =================================================
platform darwin -- Python 3.7.1 pytest-4.0.2, py-1.7.0, pluggy-0.8.0 -- /Users/gaston/HillarPythonREST2/Tornado01/bin/python3
cachedir: .pytest_cache
rootdir: /Users/gaston/HillarPythonREST2/Tornado01, inifile: 
setup.cfg
plugins: tornasync-0.5.0, cov-2.6.0 collected 4 items tests.py::test_set_and_get_leds_brightness_levels PASSED [ 25%] tests.py::test_set_and_get_hexacopter_motor_speed PASSED [ 50%] tests.py::test_get_altimeter_altitude_in_feet PASSED [ 75%] tests.py::test_get_altimeter_altitude_in_meters PASSED [100%] ---------- coverage: platform darwin, python 3.7.1-final-0 ----------- Name Stmts Miss Branch BrPart Cover ---------------------------------------------------------- async_drone_service.py 142 41 20 8 69% drone.py 63 10 10 5 79% ---------------------------------------------------------- TOTAL 205 51 30 13 72%

The output provided details indicating that the test runner executed four tests and all of them passed. The test code coverage measurement report provided by the coverage package increased the Cover percentage of the async_drone_service.py module from 40% to 69%. In addition, the Cover percentage of the drone.py module increased from 59% in the previous run to 79%. The new tests we wrote executed additional code in different modules, and therefore there is an important impact in the coverage report. The total coverage increased from 46% to 72%.

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 that we receive an HTTP 404 Not Found status code when we try to access a resource that doesn't exist.

Open the tests.py file in the root folder for the virtual environment (Tornado01). Insert the following lines after the last line. The code file for the sample is included in the restful_python_2_11_03 folder, in the Django01/tests.py file:

async def test_set_invalid_brightness_level(http_server_client): 
    """ 
    Ensure we cannot set an invalid brightness level for a LED 
    """ 
    patch_args_led_1 = {'brightness_level': 256} 
    try: 
        patch_response_led_1 = await http_server_client.fetch( 
            '/leds/1',  
            method='PATCH',  
            body=json.dumps(patch_args_led_1)) 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.BAD_REQUEST 
    patch_args_led_2 = {'brightness_level': -256} 
    try: 
        patch_response_led_2 = await http_server_client.fetch( 
            '/leds/2',  
            method='PATCH',  
            body=json.dumps(patch_args_led_2)) 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.BAD_REQUEST 
    patch_args_led_3 = {'brightness_level': 512} 
    try: 
        patch_response_led_3 = await http_server_client.fetch( 
            '/leds/3',  
            method='PATCH',  
            body=json.dumps(patch_args_led_3)) 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.BAD_REQUEST 
 
 
async def 
test_set_brightness_level_invalid_led_id(http_server_client): """ Ensure we cannot set the brightness level for an invalid LED id """ patch_args_led_1 = {'brightness_level': 128} try: patch_response_led_1 = await http_server_client.fetch( '/leds/100', method='PATCH', body=json.dumps(patch_args_led_1)) except HTTPClientError as err: assert err.code == HTTPStatus.NOT_FOUND async def
test_get_brightness_level_invalid_led_id(http_server_client): """ Ensure we cannot get the brightness level for an invalid LED id """ try: patch_response_led_1 = await http_server_client.fetch( '/leds/100', method='GET') except HTTPClientError as err: assert err.code == HTTPStatus.NOT_FOUND

The previous code added the following three test functions, whose names start with the test_ prefix and receive the http_server_client argument to use this test fixture:

  • test_set_invalid_brightness_level: This test function makes sure that we cannot set an invalid brightness level for an LED through an HTTP PATCH request. In this method, many try...except blocks capture an HTTPClientError exception as err and use assert to make sure the err.code attribute matches HTTPStatus.BAD_REQUEST. This way, the test makes sure that each HTTP PATCH request generates an HTTP 400 Bad Request status code.
  • test_set_brightness_level_invalid_led_id: This test function 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 test function makes sure that we cannot get the brightness level for an invalid LED id through an HTTP GET request.

In the last two methods, a try...except block captures an HTTPClientError exception as err. The except block uses assert to make sure the err.code attribute matches HTTPStatus.NOT_FOUND. This way, the test makes sure that the HTTP PATCH and HTTP GET requests to generate an HTTP 404 Not Found status code.

When an HTTP request is unsuccessful, the http_server_client.fetch method raises a tornado.httpclient.HTTPClientError exception and the status code is available in the code attribute for the instance.

Stay in the tests.py file in the root folder for the virtual environment (Tornado01). Insert the following lines after the last line. The code file for the sample is included in the restful_python_2_11_03 folder, in the Django01/tests.py file:

async def test_set_invalid_motor_speed(http_server_client): 
    """ 
    Ensure we cannot set an invalid motor speed for the hexacopter 
    """ 
    patch_args_hexacopter_1 = {'motor_speed': 89000} 
    try: 
        patch_response_hexacopter_1 = await http_server_client.fetch( 
            '/hexacopters/1',  
            method='PATCH',  
            body=json.dumps(patch_args_hexacopter_1)) 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.BAD_REQUEST 
    patch_args_hexacopter_2 = {'motor_speed': -78600} 
    try: 
        patch_response_hexacopter_2 = await http_server_client.fetch( 
            '/hexacopters/1',  
            method='PATCH',  
            body=json.dumps(patch_args_hexacopter_2)) 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.BAD_REQUEST 
    patch_args_hexacopter_3 = {'motor_speed': 8900} 
    try: 
        patch_response_hexacopter_3 = await http_server_client.fetch( 
            '/hexacopters/1',  
            method='PATCH',  
            body=json.dumps(patch_args_hexacopter_3)) 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.BAD_REQUEST 
 
 
async def test_set_motor_speed_invalid_hexacopter_id(http_server_client): 
    """ 
    Ensure we cannot set the motor speed for an invalid hexacopter id 
    """ 
    patch_args_hexacopter_1 = {'motor_speed': 128} 
    try: 
        patch_response_hexacopter_1 = await http_server_client.fetch( 
            '/hexacopters/100',  
            method='PATCH',  
            body=json.dumps(patch_args_hexacopter_1)) 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.NOT_FOUND 
 
 
async def test_get_motor_speed_invalid_hexacopter_id(http_server_client): 
    """ 
    Ensure we cannot get the motor speed for an invalid hexacopter id 
    """ 
    try: 
        patch_response_hexacopter_1 = await http_server_client.fetch( 
            '/hexacopters/5',  
            method='GET') 
    except HTTPClientError as err: 
        assert err.code == HTTPStatus.NOT_FOUND 
 
 
async def test_get_altimeter_altitude_invalid_altimeter_id(http_server_clie
nt):
""" Ensure we cannot get the altimeter's altitude for an invalid altimeter id """ try: get_response = await http_server_client.fetch( '/altimeters/5', method='GET') except HTTPClientError as err: assert err.code == HTTPStatus.NOT_FOUND

The previous code added the following four test functions, whose names start with the test_ prefix and receive the http_server_client argument to use this test fixture:

  • test_set_invalid_brightness_level: This test function makes sure that we cannot set an invalid brightness level for the LED through an HTTP PATCH request
  • test_set_motor_speed_invalid_hexacopter_id: This test function 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 test function makes sure that we cannot get the motor speed for an invalid hexacopter id
  • test_get_altimeter_altitude_invalid_altimeter_id: This test function makes sure that we cannot get the altitude value for an invalid altimeter id

We coded many additional tests that will make sure that all the validations work as expected. Now, we will use the pytest command again to run the tests and measure their code coverage. Make sure you run the command in the Terminal or Command Prompt window in which you have activated the virtual environment, and that you are located within its root folder (Tornado01). Run the following command:

    pytest --cov -v

The following lines show the sample output:

================================================ test session starts =================================================
platform darwin -- Python 3.7.1, pytest-4.0.2, py-1.7.0, pluggy-0.8.0 -- /Users/gaston/HillarPythonREST2/Tornado01/bin/python3
cachedir: .pytest_cache
rootdir: /Users/gaston/HillarPythonREST2/Tornado01, inifile: 
setup.cfg
plugins: tornasync-0.5.0, cov-2.6.0 collected 11 items tests.py::test_set_and_get_leds_brightness_levels PASSED [ 9%] tests.py::test_set_and_get_hexacopter_motor_speed PASSED [ 18%] tests.py::test_get_altimeter_altitude_in_feet PASSED [ 27%] tests.py::test_get_altimeter_altitude_in_meters PASSED [ 36%] tests.py::test_set_invalid_brightness_level PASSED [ 45%] tests.py::test_set_brightness_level_invalid_led_id PASSED [ 54%] tests.py::test_get_brightness_level_invalid_led_id PASSED [ 63%] tests.py::test_set_invalid_motor_speed PASSED [ 72%] tests.py::test_set_motor_speed_invalid_hexacopter_id PASSED [ 81%] tests.py::test_get_motor_speed_invalid_hexacopter_id PASSED [ 90%] tests.py::test_get_altimeter_altitude_invalid_altimeter_id PASSED [100%] ------------ coverage: platform darwin, python 3.7.1-final-0 ----------- Name Stmts Miss Branch BrPart Cover ---------------------------------------------------------- async_drone_service.py 142 17 20 2 87% drone.py 63 8 10 3 85% ---------------------------------------------------------- TOTAL 205 25 30 5 86%

The output provided details indicating that the test runner executed 11 tests and all of them passed. The test code coverage measurement report provided by the coverage package increased the Cover percentage of the async_drone_service.py module from 69% to 87%. In addition, the Cover percentage of the drone.py module increased from 79% in the previous run to 85%. The new tests we wrote executed additional code in different modules, and therefore there is an important impact in the coverage report. The total coverage increased from 72% to 86%.

..................Content has been hidden....................

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