Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package checks.tests;

import org.junit.jupiter.api.AfterEach;

public class AssertStatementInTestCheckSample {

@org.junit.Test
public void verifiesWithJUnit4() {
assert Account.activeCount() == 1; // Noncompliant {{Replace this "assert" statement with an assertion from the testing framework.}}
// ^^^^^^
org.junit.Assert.assertEquals(1, Account.activeCount());
}

@org.junit.jupiter.api.Test
void verifiesWithJupiter() {
assert Account.activeCount() == 1 : "an account should be active"; // Noncompliant
// ^^^^^^
org.junit.jupiter.api.Assertions.assertEquals(1, Account.activeCount(), "an account should be active");
}

@org.testng.annotations.Test
public void verifiesWithTestNg() {
assert Account.activeCount() == 1; // Noncompliant
// ^^^^^^
org.testng.Assert.assertEquals(Account.activeCount(), 1);
}

@org.junit.jupiter.api.Test
void verifiesInControlFlow() {
if (Account.activeCount() > 0) {
assert Account.activeCount() == 1; // Noncompliant
// ^^^^^^
}
}

@org.junit.jupiter.api.TestTemplate
void verifiesInLambda() {
Runnable verification = () -> {
assert Account.activeCount() == 1; // Noncompliant
// ^^^^^^
};
verification.run();
}

@AfterEach
void resetState() {
assert Account.activeCount() == 0;
}

void helper() {
assert Account.activeCount() >= 0;
}

@Test
void customTestAnnotationIsIgnored() {
assert Account.activeCount() == 1;
}

@interface Test {
}

static class Account {
static int activeCount() {
return 1;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* SonarQube Java
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.java.checks.tests;

import java.util.List;
import org.sonar.check.Rule;
import org.sonar.java.model.ExpressionUtils;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.tree.AssertStatementTree;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.tree.Tree;

import static org.sonar.java.utils.UnitTestUtils.hasTestAnnotation;

@Rule(key = "S9427")
public class AssertStatementInTestCheck extends IssuableSubscriptionVisitor {

private static final String MESSAGE = "Replace this \"assert\" statement with an assertion from the testing framework.";

@Override
public List<Tree.Kind> nodesToVisit() {
return List.of(Tree.Kind.ASSERT_STATEMENT);
}

@Override
public void visitNode(Tree tree) {
var assertStatement = (AssertStatementTree) tree;
var enclosingTree = ExpressionUtils.getEnclosingTree(assertStatement, Tree.Kind.METHOD);
if (enclosingTree instanceof MethodTree method && hasTestAnnotation(method)) {
reportIssue(assertStatement.assertKeyword(), MESSAGE);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SonarQube Java
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.java.checks.tests;

import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;

import static org.sonar.java.checks.verifier.TestUtils.testCodeSourcesPath;

class AssertStatementInTestCheckTest {

private static final String SAMPLE = testCodeSourcesPath("checks/tests/AssertStatementInTestCheckSample.java");
private static final AssertStatementInTestCheck CHECK = new AssertStatementInTestCheck();

@Test
void test() {
CheckVerifier.newVerifier()
.onFile(SAMPLE)
.withCheck(CHECK)
.verifyIssues();
}

@Test
void test_without_semantic() {
CheckVerifier.newVerifier()
.onFile(SAMPLE)
.withCheck(CHECK)
.withoutSemantic()
.verifyIssues();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<p>This rule raises an issue when a language’s built-in assertion statement is used within a test method, identified by test framework annotations or
markers that designate methods as test cases.</p>
<h2>Why is this an issue?</h2>
<p>Native assertion statements in many languages are inappropriate for test code because they can be disabled or controlled by runtime configuration.
Unless explicitly enabled through compiler flags, runtime options, or environment settings, these assertion statements may do nothing—your test will
pass even when the condition fails.</p>
<p>This creates a false sense of security. Tests that appear to pass may not actually be verifying anything.</p>
<p>Additionally, when native assertions do execute, they provide minimal diagnostic information. A failed assertion typically throws a generic error
with little context about what went wrong. In contrast, testing framework assertion methods provide:</p>
<ul>
<li>Clear failure messages showing expected vs. actual values</li>
<li>Stack traces pointing to the exact assertion that failed</li>
<li>Better IDE integration for debugging</li>
<li>Consistent behavior regardless of runtime configuration</li>
</ul>
<p>Testing frameworks are specifically designed for writing tests and handle assertions properly in all execution modes.</p>
<h3>What is the potential impact?</h3>
<p>Using assertion mechanisms that can be disabled through runtime configuration in tests can lead to:</p>
<ul>
<li><strong>Silent test failures</strong>: Tests that don’t actually verify anything because assertions are disabled through configuration
flags</li>
<li><strong>Difficult debugging</strong>: When assertions do fail, minimal error messages make it hard to understand what went wrong</li>
<li><strong>Inconsistent behavior</strong>: Test results that vary depending on runtime configuration settings</li>
<li><strong>Reduced maintainability</strong>: Other developers may not realize tests aren’t actually running assertions</li>
</ul>
<h2>How to fix it in JUnit 4</h2>
<p>Replace the Java <code>assert</code> statement with JUnit 4’s static assertion methods from the <code>org.junit.Assert</code> class. Use methods
like <code>assertEquals()</code>, <code>assertTrue()</code>, <code>assertFalse()</code>, <code>assertNull()</code>, etc., which provide clear error
messages and always execute.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<pre data-diff-id="1" data-diff-type="noncompliant">
import org.junit.Test;

public class FooTest {
@Test
public void testSomething() {
assert Foo.bar == 1; // Noncompliant
}
}
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class FooTest {
@Test
public void testSomething() {
assertEquals(1, Foo.bar);
}
}
</pre>
<h2>How to fix it in JUnit 5</h2>
<p>Replace the Java <code>assert</code> statement with JUnit 5’s static assertion methods from the <code>org.junit.jupiter.api.Assertions</code>
class. JUnit 5 provides improved assertion methods with better messages and optional custom failure messages.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<pre data-diff-id="2" data-diff-type="noncompliant">
import org.junit.jupiter.api.Test;

public class FooTest {
@Test
void testSomething() {
assert Foo.bar == 1; // Noncompliant
}
}
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="2" data-diff-type="compliant">
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class FooTest {
@Test
void testSomething() {
assertEquals(1, Foo.bar, "Foo.bar should be 1");
}
}
</pre>
<h2>How to fix it in TestNG</h2>
<p>Replace the Java <code>assert</code> statement with TestNG’s static assertion methods from the <code>org.testng.Assert</code> class. TestNG
assertions provide detailed failure messages and integrate with TestNG’s reporting features.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<pre data-diff-id="3" data-diff-type="noncompliant">
import org.testng.annotations.Test;

public class FooTest {
@Test
public void testSomething() {
assert Foo.bar == 1; // Noncompliant
}
}
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="3" data-diff-type="compliant">
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;

public class FooTest {
@Test
public void testSomething() {
assertEquals(Foo.bar, 1);
}
}
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
<li>JUnit 4 Documentation - <a href="https://junit.org/junit4/javadoc/latest/org/junit/Assert.html">Assertions</a></li>
<li>JUnit 5 Documentation - <a
href="https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html">Assertions</a></li>
<li>TestNG Documentation - <a href="https://javadoc.io/doc/org.testng/testng/latest/org/testng/Assert.html">Assert class</a></li>
<li>Oracle Java Documentation - <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html">Programming With
Assertions</a></li>
</ul>

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"title": "Java assert statements should not be used in test code",
"type": "BUG",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"junit",
"assertion",
"tests"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-9427",
"sqKey": "S9427",
"scope": "Tests",
"quickfix": "unknown",
"code": {
"impacts": {
"RELIABILITY": "HIGH"
},
"attribute": "COMPLETE"
}
}
Empty file.
Loading