PyNose is a test smell detector tool for Python. It runs as a plugin inside the PyCharm IDE (version 2021.3)
Note: PyNose is currently under active development, the older version of the tool that was initially described in the paper "PyNose: A Test Smell Detector For Python" can be found in the ASE2021
branch.
- Download the latest release of the plugin from here;
- Open PyCharm and go to
File
/Settings
/Plugins
; - Select the gear icon, and choose
Install Plugin from Disk...
; - Choose the downloaded ZIP archive;
- Click
Apply
; - Restart the IDE.
PyNose can be used inside the IDE to study the test smells within a specific opened project. This can help python developers avoid test smells in their code.
When you open a project in PyCharm, a number of inspections will be available for usage.
PyNose will retrieve the information about your currently configured Test Runner (Preferences > Tools > Python Integrated Tools > Testing > Default test runner
) to use the appropriate set of inspections (pytest or Unittest).
Some inspections are initially disabled, however you can enable them from the settings.
Inspections | Unittest | Pytest | Quick fix | Warning level | Description |
---|---|---|---|---|---|
Assertion Roulette | + | + | - | Disabled* | A test case contains more than one assertion statement without an explanation |
Conditional Test Logic | + | + | - | Disabled | Presence of control statements (i.e., if, for, while) |
Constructor Initialization | + | - | + (move logic to setup) | Weak warning | A test suite contains a constructor declaration (an __init__ method) |
Default Test | + | - | + (suggest rename refactoring) | Weak warning | A test suite is called MyTestCase |
Duplicate Assert | + | + | + (remove duplicate) | Warning | Occurrence of more than one assertion statement with the same parameters |
Empty Test | + | + | + (safe delete) | Warning | A test case does not contain a single executable statement |
Exception Handling | + | + | + (replace with framework raise handler) | Warning | Presence of either the try/except statement or the raise statement |
Lack of Cohesion of Test Cases | + | + | - | Disabled | Test suites in a test case are not cohesive according to pairwise cosine similarities metric |
Magic Number Test | + | + | - | Disabled | Presence of an assertion statement with a numeric literal as an argument |
Obscure In-Line Setup | + | + | - | Disabled | A test case contains ten or more local variables declarations |
Redundant Assertion | + | + | + (remove assertion) | Warning | Presence of assertions the result of which never changes (i.e., assert 1 == 1) |
Redundant Print | + | + | + (remove statement) | Disabled | print() function invocation |
Sleepy Test | + | + | + (remove statement) | Disabled | time.sleep() function invocation |
Suboptimal Assert | + | - | + (replace with optimal) | Warning | Presence of one of the suboptimal asserts** |
Test Maverick | + | + | - | Disabled | A test suite contains at least one test case that does not use a single field from the setup method |
*Disabled by default. If enabled — weak warning.
**List of suboptimal assertions is available here.
If you have any questions or suggestions, don't hesitate to open an issue or contact Yaroslav Golubev at yaroslav.golubev@jetbrains.com
.