Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- **Bad practices** I want to avoid by this guidelines:
- 1. Creating a separate test **class for one test case**. (It is left mostly from the `unittest` times.)
- 2. Creating test cases for **different** module-level **methods** under the **same** test **class**. (See reason #1. Then developer just copy-paste existing testcase to create a new method test case (which is ok) and place it under an existing test class (which is not ok))
- 3. **Duplicating method name** from test class in the test case name. Like this: `TestMyMethod->test_my_method_positive, test_my_method_negative` (DRY. Once you've created a test class for only one method there is no need to duplicate its name in the `test_method` name)
- 4. Method name being written with a **scenario name without a separator**. (Consider name like `test_series_length_is_more_than_10`. It doesn't look too bad until you consider that there are 2 methods in the module:
- * `series_length(series)->int`
- * `series_length_is_more_than(series, int)->bool`
- Double uderscore makes things clear: where method name ends and test case description starts:
- * `test_series_length__is_more_than_10`
- * `test_series_length_is_more_than__10`
- ## Suggested guidelines
- **1. Test module has the same path as the source module.** E.g.: `ModelingMachine/worker.py` -> `ModelingMachine/test__worker.py`
- **2. Keep test (and source) files shorter than 1000 lines.** If it grows too big (not decided):
- * create new files with `__description_in_the_name` (plain is better than nested). E.g.:
- * `ModelingMachine/worker.py (ModelingMachineWorkerProcess class)` -> `ModelingMachine/test__worker__modeling_machine_worker_process.py`
- * `ModelingMachine/worker.py (ProcessUpdater class)` -> `ModelingMachine/test__worker__process_updater.py`
- * `ModelingMachine/worker.py (SecureWorkerProcess class)` -> `ModelingMachine/test__worker__secure_worker_process.py`
- * `ModelingMachine/worker.py (the rest)` -> `ModelingMachine/test__worker.py`
- * create a new package. (source module can be divided later the same way) E.g.:
- * `ModelingMachine/worker.py (ModelingMachineWorkerProcess class)` -> `ModelingMachine/worker/test__common.py`
- * `ModelingMachine/worker.py (ProcessUpdater class)` -> `ModelingMachine/worker/test__modeling_machine_worker_process.py`
- * `ModelingMachine/worker.py (SecureWorkerProcess class)` -> `ModelingMachine/worker/test__process_updater.py`
- * `ModelingMachine/worker.py (the rest)` -> `ModelingMachine/worker/test__secure_worker_process.py`
- **3. Structure of the test file**:
- * Module-level methods are tested with module-level test cases.
- * Class-level methods are tested with class-level test cases.
- but
- * you can create a separate class for a complex method to keep file structured.
- **4. Testcase method name structure**: `def test_{method name}__{scenario description}__{expected result}(...)`:
- `{method name}` - required.
- `{scenario description}` - required if there are multiple test cases for a method.
- `{expected result}` - strongly suggested if there are >5 scenarios for a method.
- ```
- --- original_file.py ---
- def simple_method():
- ...
- def standard_method():
- # positive -> 0
- # negative -> 1
- def complex_method():
- # -> {"status": 0, "message": "ok"}
- # -> {"status": 10, "message": "Need info"}
- # -> {"status": 20, "message": "Permission Denied"}
- # -> {"status": 100, "message": "Error"}
- class A:
- def do_something():
- ...
- def do_another_thing():
- ...
- def method_with_extremely_complex_logic_avoid_creating_such_methods():
- ...
- ```
- ```
- --- test_original_file.py ---
- # Keep simple things simple. One test case can be enough.
- def test_simple_method():
- ...
- # It is not required to specify expected result but you make the world better by doing this.
- def test_standard_method__positive__0():
- ...
- def test_standard_method__negative__1():
- ...
- # Some *invalid input* will be provided for the *standard_method*. It is expected to raise *ValueError*.
- def test_standard_method__invalid_input__value_error():
- ...
- class TestComplexMethod(object):
- def test_positive__status_0():
- ....
- def test_no_phone__status_10():
- ....
- def test_wrong_user__status_20():
- ....
- def test_user_does_not_exist__status_100():
- ....
- class TestA(object):
- def test_do_something():
- ...
- # You can skip test casse description for default/positive scenario.
- def test_do_another_thing():
- ...
- def test_do_another_thing__wrong_state__value_error():
- ...
- class TestAMethodWithExtremelyComplexLogicAvoidCreatingSuchMethods(object):
- # Class name already contains both source class name and source method name.
- # Method name is skipped in the test case method name.
- def test_description_1__expected_output_or_side_efect_1():
- ...
- def test_description_2__expected_output_or_side_efect_2():
- ...
- ...
- def test_description_n__expected_output_or_side_efect_n():
- ...
- ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement