Giter VIP home page Giter VIP logo

pynose's Introduction

PyNose: a Test Smell Detector for Python

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.

How to use in PyCharm

  1. Download the latest release of the plugin from here;
  2. Open PyCharm and go to File/Settings/Plugins;
  3. Select the gear icon, and choose Install Plugin from Disk...;
  4. Choose the downloaded ZIP archive;
  5. Click Apply;
  6. Restart the IDE.

Description

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.

Available inspections

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.

Usage examples

  • Suboptimal assertion detection and quick fix

    Suboptimal assertion example

  • Exception handling detection and quick fix

    Exception handling example

Contacts

If you have any questions or suggestions, don't hesitate to open an issue or contact Yaroslav Golubev at [email protected].

pynose's People

Contributors

areyde avatar olesiasub avatar smirnovoleg avatar spirinegor avatar wangjieke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

pynose's Issues

False positive "Consider removing conditions" for comprehensions

@pytest.mark.parametrize(
    ("number", "expected"),
    [
        (1, 0),
        *((i, 1) for i in range(2, 10)),
        *((i, 2) for i in range(10, 26)),
        *((i, 3) for i in range(26, 50)),
        (50, 4),
    ],
)
def test_circle_number(number, expected):
    assert get_circle_number(number) == expected

Plugin reports

Consider removing conditions from tests to ensure that all statements are executed

warning

pyNose CLI

Hi,

Is there anywhere I can find an example command on how to run the tools in CLI because I saw that in the papers but I don't see it anywhere in the repo.

ArrayIndexOutOfBoundsException

java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at java.base/java.util.Arrays$ArrayList.get(Arrays.java:4351)
at org.jetbrains.research.pynose.plugin.inspections.unittest.RedundantAssertionTestSmellUnittestInspection$buildUnittestVisitor$1.visitPyCallExpression(RedundantAssertionTestSmellUnittestInspection.kt:29)
at com.jetbrains.python.psi.impl.PyCallExpressionImpl.acceptPyVisitor(PyCallExpressionImpl.java:29)
at com.jetbrains.python.psi.impl.PyBaseElementImpl.accept(PyBaseElementImpl.java:69)
at com.intellij.codeInspection.InspectionEngine.acceptElements(InspectionEngine.java:65)
at com.intellij.codeInspection.InspectionEngine.createVisitorAndAcceptElements(InspectionEngine.java:56)
at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.runToolOnElements(LocalInspectionsPass.java:320)
at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.lambda$visitPriorityElementsAndInit$4(LocalInspectionsPass.java:279)
at com.intellij.util.AstLoadingFilter.forceAllowTreeLoading(AstLoadingFilter.java:161)
at com.intellij.util.AstLoadingFilter.forceAllowTreeLoading(AstLoadingFilter.java:153)
at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.lambda$visitPriorityElementsAndInit$5(LocalInspectionsPass.java:277)
at com.intellij.util.AstLoadingFilter.disallowTreeLoading(AstLoadingFilter.java:132)
at com.intellij.util.AstLoadingFilter.disallowTreeLoading(AstLoadingFilter.java:121)
at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.lambda$visitPriorityElementsAndInit$6(LocalInspectionsPass.java:277)
at com.intellij.concurrency.ApplierCompleter.execAndForkSubTasks(ApplierCompleter.java:136)
at com.intellij.concurrency.ApplierCompleter.execAndForkSubTasks(ApplierCompleter.java:149)
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1096)
at com.intellij.concurrency.ApplierCompleter.lambda$wrapInReadActionAndIndicator$1(ApplierCompleter.java:92)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:688)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:634)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:64)
at com.intellij.concurrency.ApplierCompleter.wrapInReadActionAndIndicator(ApplierCompleter.java:104)
at com.intellij.concurrency.ApplierCompleter.lambda$compute$0(ApplierCompleter.java:83)
at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:167)
at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:178)
at com.intellij.concurrency.ApplierCompleter.compute(ApplierCompleter.java:83)
at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

Change plugin-utilities dependency declaration

!!!Actions required!!!
We have published current plugin-utilities lib version from master branch to space maven repository and in a week planning to modify api (so master branch will become invalid). Please, replace plugin-utilities lib git-based installation in your build.gradle.kts with (standard dependency declaration) by adding space repository and declaring required dependencies:

repositories {
    maven(“https://packages.jetbrains.team/maven/p/big-code/bigcode”)
}

dependencies {
    implementation(“org.jetbrains.research:plugin-utilities-core:1.0")
    implementation(“org.jetbrains.research:plugin-utilities-test:1.0")
    implementation(“org.jetbrains.research:plugin-utilities-python:1.0")
}

Consider renaming the project

There is a well-known Python test runner called nose (not super trendy today though). The name collision is quite unfortunate.

  • Both the plugin and the package have a similar application field (unit testing)
  • PyNose plugin doesn't actually support nose, which is somewhat counterintuitive based on its name

Changed test-runner to Unittests in PyCharm 2021.3 #PY-213.5744.248 on macOS

java.lang.Throwable: 'this constructor' is deprecated and going to be removed soon. 
	at com.intellij.openapi.diagnostic.Logger.error(Logger.java:182)
	at com.intellij.diagnostic.PluginException.reportDeprecatedUsage(PluginException.java:105)
	at com.jetbrains.python.inspections.PyInspectionVisitor.<init>(PyInspectionVisitor.java:52)
	at org.jetbrains.research.pynose.plugin.inspections.common.disabled.ObscureInLineSetupTestSmellVisitor.<init>(ObscureInLineSetupTestSmellVisitor.kt:16)
	at org.jetbrains.research.pynose.plugin.inspections.unittest.disabled.ObscureInLineSetupTestSmellUnittestInspection$buildUnittestVisitor$1.<init>(ObscureInLineSetupTestSmellUnittestInspection.kt:17)
	at org.jetbrains.research.pynose.plugin.inspections.unittest.disabled.ObscureInLineSetupTestSmellUnittestInspection.buildUnittestVisitor(ObscureInLineSetupTestSmellUnittestInspection.kt:17)
	at org.jetbrains.research.pynose.plugin.inspections.AbstractTestSmellInspection.buildVisitor(AbstractTestSmellInspection.kt:33)
	at com.intellij.codeInspection.InspectionEngine.createVisitor(InspectionEngine.java:57)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.createContext(LocalInspectionsPass.java:474)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.lambda$visitPriorityElementsAndInit$3(LocalInspectionsPass.java:349)
	at com.intellij.util.containers.ContainerUtil.mapNotNull(ContainerUtil.java:1934)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.visitPriorityElementsAndInit(LocalInspectionsPass.java:349)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.inspect(LocalInspectionsPass.java:227)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.collectInformationWithProgress(LocalInspectionsPass.java:127)
	at com.intellij.codeInsight.daemon.impl.ProgressableTextEditorHighlightingPass.doCollectInformation(ProgressableTextEditorHighlightingPass.java:84)
	at com.intellij.codeHighlighting.TextEditorHighlightingPass.collectInformation(TextEditorHighlightingPass.java:56)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$1(PassExecutorService.java:414)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1084)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$2(PassExecutorService.java:407)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$12(CoreProgressManager.java:624)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:698)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:646)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:623)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:66)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.doRun(PassExecutorService.java:406)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$run$0(PassExecutorService.java:382)
	at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:174)
	at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:181)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.run(PassExecutorService.java:380)
	at com.intellij.concurrency.JobLauncherImpl$VoidForkJoinTask$1.exec(JobLauncherImpl.java:184)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

Tool does not detect Magic Numbers (unittest)

PyNose skips identifying numeric literal within the assertion statement as magic numbers.

image

Here is the settings in Editor > Inspections

image

Tool used: pyNose 1.0.0
IDE: PyCharm Community 2022.2.3
Python: 3.10

PyNose as a python package

Hello.

Do you have any plans to use this plugin as separate entity, for example flake8-pynose or self-sufficient python package? For include this features in CI pipeline or precommit hook?

Thanks.

Duplicate assertion used by with

In cases like this duplicate assertion is detected and removed incorrectly.

with self.assertRaises(ValueError):
    x = 3
with self.assertRaises(ValueError):
    x = 4

leads to this:

with:
    x = 4

Useless tab

Screen.Recording.2021-12-06.at.21.03.07.mov

Data Clumps Support?

Hi there,

currently in my PhD research I am facing data clumps to be refactored. Maybe you will consider this code smell too, as it is also a design smell.

I am currently developing a IntelliJ IDEA plugin.
I am facing the problem to load all dependencies and getting the qualified name of a class.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.