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.

