|
| 1 | +[README.md](./README.md) > [questionnaire_unit_tests.md](./questionnaire_unit_tests.md) |
| 2 | + |
| 3 | +# Questionnaire_unit_tests.py |
| 4 | + |
| 5 | +Uses the 'unittest' automated framework but not only to test questionnaire.py |
| 6 | + |
| 7 | +```commandline |
| 8 | +python ./questionnaire_unit_tests.py |
| 9 | +``` |
| 10 | +--- |
| 11 | +### Level 1 : Basic Unit tests about the arguments received by questionnaire.py |
| 12 | + |
| 13 | +the argument are read and handled by the `load_json_argv` function which should return the json data `<Json_Data>` |
| 14 | +and the file path `<File_Path>` |
| 15 | + |
| 16 | +`test number`->`returned values` if receives __condition given in argument__ and prints `message` |
| 17 | + |
| 18 | +- `test_01` -> `<Json_Data>, <File_Path>` if receives __a good json file__ and prints no message |
| 19 | +```commandline |
| 20 | +questionnaire.py initial_json_test_file.json -> Json_Data, File_Path |
| 21 | +``` |
| 22 | + |
| 23 | +- `test_02` -> `None, None` if receives __no file__ and prints a message. |
| 24 | +```commandline |
| 25 | +questionnaire.py -> None, None |
| 26 | +Error : Error in argument, should add a single .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 27 | +``` |
| 28 | + |
| 29 | +- `test_03` -> `None, None` if receives __more than one file__ and prints a message. |
| 30 | +```commandline |
| 31 | +questionnaire.py initial_json_test_file.json.json second_json_test_file.json -> None, None |
| 32 | +Error : Error in argument, should add a single .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 33 | +``` |
| 34 | + |
| 35 | +- `test_04` -> `None, <File_Path>` if receives __a file without json extension__ and prints a message. |
| 36 | +```commandline |
| 37 | +questionnaire.py json_file_without_extension -> None, File_Path |
| 38 | +Error : Incorrect extension, add a .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 39 | +``` |
| 40 | + |
| 41 | +- `test_05` -> `None, <File_Path>` if receives __a non-existent file__ and prints a message. |
| 42 | +```commandline |
| 43 | +questionnaire.py non-existent-file.json -> None, File_Path |
| 44 | +Error : Invalid permissions or path to non-existent-file.json |
| 45 | +``` |
| 46 | + |
| 47 | +- `test_06` -> `None, <File_Path>` if receives __an empty json file__ and prints a message. |
| 48 | +```commandline |
| 49 | +questionnaire.py empty.json -> None, File_Path |
| 50 | +Error : Incompatible or no data in JSON file empty.json |
| 51 | +``` |
| 52 | + |
| 53 | +--- |
| 54 | +### Level 2 : Same tests but with a different name of program |
| 55 | +If the name of the questionnaire.py change, the error messages should adapt.\ |
| 56 | +The class test is derived from the level 1 but simulates a program named `test.py` |
| 57 | + |
| 58 | +--- |
| 59 | +### Level 3 : Same tests but with a file into a folder |
| 60 | + |
| 61 | +The class derives from the level 1 and executes the same tests except that |
| 62 | +a [json_file_tests](./json_file_tests) folder is added to the path of the file given in argument |
| 63 | + |
| 64 | +The printed message of test 2 is quite different from the Level 1 since the file path is given in argument. |
| 65 | +- `test_02` -> `None, <File_Path>` if receives __no file__ and prints a message |
| 66 | +```commandline |
| 67 | +questionnaire.py json_file_tests/ -> None, File_Path |
| 68 | +Error : Incorrect extension, add a .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 69 | +``` |
| 70 | +--- |
| 71 | +### Level 4 : Same tests but with another name and a file into a folder |
| 72 | + |
| 73 | +The class derives from the level 2 and 3 and executes the same tests under `test.py` name and with files |
| 74 | +from [json_file_tests](./json_file_tests) |
| 75 | + |
| 76 | +The printed message of test 2 is so quite different from the Level 1 as well. |
| 77 | +- `test_02` -> `None, <File_Path>` if receives __no file__ and prints a message. |
| 78 | +```commandline |
| 79 | +test.py json_file_tests/ -> None, File_Path |
| 80 | +Error : Incorrect extension, add a .json questionnaire. Example : test.py questionnaire_to_read.json |
| 81 | +``` |
| 82 | + |
| 83 | +--- |
| 84 | +### Level 5 : Tests with data online |
| 85 | + |
| 86 | +A free json file is available at the url : https://countwordsfree.com/example.json |
| 87 | + |
| 88 | +- `test_01` -> `<Json_Data>, <File_Path>` if receives __a valid json file on a valid url path__ and prints no message. |
| 89 | +```commandline |
| 90 | +questionnaire.py https://countwordsfree.com/example.json -> Json_Data, File_Path |
| 91 | +``` |
| 92 | + |
| 93 | +- `test_02` -> `None, <File_Path>` if receives __a invalid json file on a valid url path__ and prints a message. |
| 94 | +```commandline |
| 95 | +questionnaire.py https://countwordsfree.com/non-existant.json -> None, File_Path |
| 96 | +Error : File not found at URL https://countwordsfree.com/non-existant.json |
| 97 | +``` |
| 98 | + |
| 99 | +- `test_03` -> `None, <File_Path>` if receives __a valid json file but on a invalid url path__ and prints a message. |
| 100 | +```commandline |
| 101 | +questionnaire.py https://countwordsfre.com/example.json -> None, File_Path |
| 102 | +Error : Invalid URL at https://countwordsfre.com/example.json |
| 103 | +``` |
| 104 | + |
| 105 | +- `test_04` -> `None, <File_Path>` if receives __a file without json extension on a valid url path__ and prints a message. |
| 106 | +```commandline |
| 107 | +questionnaire.py https://countwordsfree.com/empty_or_uncompleted_file -> None, File_Path |
| 108 | +Error : Incorrect extension, add a .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 109 | +``` |
| 110 | + |
| 111 | +--- |
| 112 | +### Level 6 : Tests with data on http://localhost |
| 113 | + |
| 114 | +To go further and test wrong or empty json files, it is necessary to simulate a simple HTTP server |
| 115 | +via localhost(`http://127.0.0.1`) for example. |
| 116 | + |
| 117 | +The simple HTTP server is started through a thread by the `setUp` method and close by the `tearDown` method. Those |
| 118 | +2 `unittest.TestCase` methods are respectively called before and after each test of the class and can be overwritten. |
| 119 | + |
| 120 | +The class derives from the level 1 and therefore executes the same tests but through an HTTP connection. |
| 121 | + |
| 122 | +- `test_01` -> `<Json_Data>, <File_Path>` if receives __a good json file__ and prints no message. |
| 123 | +```commandline |
| 124 | +questionnaire.py http://127.0.0.1:8000/initial_json_test_file.json -> Json_Data, File_Path |
| 125 | +127.0.0.1 - - [02/Dec/2022 12:11:53] "GET /initial_json_test_file.json HTTP/1.1" 200 - |
| 126 | +``` |
| 127 | + |
| 128 | +The printed message of test 2 is quite different from the Level 1 since the URL is given in argument. |
| 129 | +- `test_02` -> `None, <File_Path>` if receives __no file__ and prints a message |
| 130 | +```commandline |
| 131 | +questionnaire.py http://127.0.0.1:8000/ -> None, File_Path |
| 132 | +Error : Incorrect extension, add a .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 133 | +``` |
| 134 | + |
| 135 | +- `test_03` -> `None, None` if receives __more than one file__ and prints a message. |
| 136 | +```commandline |
| 137 | +questionnaire.py http://127.0.0.1:8000/initial_json_test_file.json.json http://127.0.0.1:8000/second_json_test_file.json -> None, None |
| 138 | +Error : Error in argument, should add a single .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 139 | +``` |
| 140 | + |
| 141 | +- `test_04` -> `None, File_Path` if receives __a file without json extension__ and prints a message. |
| 142 | +```commandline |
| 143 | +questionnaire.py http://127.0.0.1:8000/json_file_without_extension -> None, File_Path |
| 144 | +Error : Incorrect extension, add a .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 145 | +``` |
| 146 | + |
| 147 | +- `test_05` -> `None, File_Path` if receives __a non-existent file__ and prints a message. |
| 148 | +```commandline |
| 149 | +questionnaire.py http://127.0.0.1:8000/non-existent-file.json -> None, File_Path |
| 150 | +Error : File not found at URL http://127.0.0.1:8000/non-existent-file.json |
| 151 | +127.0.0.1 - - [02/Dec/2022 12:22:31] code 404, message File not found |
| 152 | +127.0.0.1 - - [02/Dec/2022 12:22:31] "GET /non-existent-file.json HTTP/1.1" 404 - |
| 153 | +``` |
| 154 | +- `test_0x` -> `None, File_Path` if receives __an empty json file__ and prints a message. |
| 155 | +```commandline |
| 156 | +questionnaire.py http://127.0.0.1:8000/empty.json -> None, File_Path |
| 157 | +Error : Incompatible or no data in JSON file at URL http://127.0.0.1:8000/empty.json |
| 158 | +127.0.0.1 - - [02/Dec/2022 12:22:31] "GET /empty.json HTTP/1.1" 200 - |
| 159 | +``` |
| 160 | +--- |
| 161 | +### Level 7 : Optimized Level 6 with pytest |
| 162 | + |
| 163 | +Rather than opening and closing an HTTP server for each test, it would be better to open and close it once for all tests |
| 164 | +in the class. |
| 165 | + |
| 166 | +This needs to create a fixture defined on the `class` scope and to use the option `autouse=True` |
| 167 | + |
| 168 | +The fixture will so open the server, before all the tests run, and will be close the server when it will be called a |
| 169 | +second time after that all tests ran. |
| 170 | +```commandline |
| 171 | +HTTP Connection to http://127.0.0.1:8000/ in Thread-4 (serve_forever) |
| 172 | +questionnaire.py http://127.0.0.1:8000/json_file_tests/initial_json_test_file.json -> Json_Data, File_Path |
| 173 | +127.0.0.1 - - [05/Dec/2022 15:28:35] "GET /json_file_tests/initial_json_test_file.json HTTP/1.1" 200 - |
| 174 | +PASSED |
| 175 | +questionnaire_unit_test.py::_07_load_json_argv_with_data_via_localhost_8000_in_fixture::test_02_load_json_argv_with_no_argv |
| 176 | +questionnaire.py http://127.0.0.1:8000/json_file_tests/ -> None, File_Path |
| 177 | +Error : Incorrect extension, add a .json questionnaire. Example : questionnaire.py questionnaire_to_read.json |
| 178 | +PASSED |
| 179 | +HTTP Disconnection from http://127.0.0.1:8000/ |
| 180 | +``` |
| 181 | +According to the definition of the fixture used in this test, this class will be executed only using `pytest` and |
| 182 | +will be skipped under `unittests` |
| 183 | +```commandline |
| 184 | +pytest -v -k questionnaire_unit_test.py -s |
| 185 | +``` |
| 186 | +--- |
| 187 | +### Level 8 : Optimized Level 6 with unittests `@classmethod` |
| 188 | + |
| 189 | +In the same idea as Level 7 with fixtures, it is possible to call the built-in methods `setUpClass` and `tearDownClass` |
| 190 | +at the beginning and at the end of the tests of the class.\ |
| 191 | +To do this, the `self` argument must be modified to `cls` because these built-in functions are private to the unittest |
| 192 | +class. It is also why it is also necessary to use the built-in tag `@classmethod` in order to access them. |
| 193 | + |
| 194 | +--- |
| 195 | +### Level 9 : Tests with a patch on network requests |
| 196 | + |
| 197 | +Unit tests should be isolated and not depend on the system or the network in order to be able, for example, to measure |
| 198 | +exactly the time needed to proceed without a external dependency. |
| 199 | + |
| 200 | +It should so better to simulate the answers of the network requests like ``Success (200)`` |
| 201 | +or ``File Not Found (404)`` for getting the best knowledge of the program behavior. |
| 202 | + |
| 203 | +This may require to rewrite the tests because the reading of the file from URL must also be simulated, like in this 2 |
| 204 | +tests trying to read files from a non-existent URL of the Web. |
| 205 | + |
| 206 | +- `test_01` -> `None, File_Path` if receives __a json file on a non-existent URL with a simulated ``File Not Found (404)`` answer to |
| 207 | +the network request__ and prints a message. |
| 208 | +```commandline |
| 209 | +questionnaire.py http://non-existent-url.com/initial_json_test_file.json -> None, File_Path |
| 210 | +Error : File not found at URL http://non-existent-url.com/initial_json_test_file.json |
| 211 | +``` |
| 212 | +- `test_02` -> `not None` if receives __a json file on a non-existent URL with a simulated ``Success (200)`` answer to the |
| 213 | +network request__ and prints no message. |
| 214 | +```commandline |
| 215 | +questionnaire.py http://non-existent-url.com/initial_json_test_file.json -> Not None (OK) |
| 216 | +``` |
| 217 | +in that case, the property ``status_code`` of the response must be updated to the code ``200 (OK)`` |
| 218 | +and the property ``text`` of the response must contain the json data read. This is simulated by the reading of a file |
| 219 | +from the given path without the server part ``./initial_json_test_file.json`` |
| 220 | + |
| 221 | +--- |
| 222 | +### More Unit Tests Documentations |
| 223 | + |
| 224 | +More information and independent tests about marks, fixtures, patchs, local server in thread, json schema, pytest.ini or |
| 225 | +conftest.py files, etc ... available in [more_unit_tests_docs](./more_unit_test_docs) folder. |
| 226 | + |
| 227 | +Thank you for reading. |
0 commit comments