Modify S3431: Promote C# rule to SonarWay (#4127)

This commit is contained in:
Sebastien Marichal 2024-08-09 10:58:55 +02:00 committed by GitHub
parent 8f7fcf7047
commit 716a7aa85d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 220 additions and 85 deletions

View File

@ -0,0 +1,33 @@
[source,csharp]
----
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void UsingTest()
{
Console.ForegroundColor = ConsoleColor.Black;
try
{
using var _ = new ConsoleAlert();
Assert.AreEqual(ConsoleColor.Red, Console.ForegroundColor);
throw new InvalidOperationException();
}
finally
{
Assert.AreEqual(ConsoleColor.Black, Console.ForegroundColor); // The exception itself is not relevant for the test.
}
}
public sealed class ConsoleAlert : IDisposable
{
private readonly ConsoleColor previous;
public ConsoleAlert()
{
previous = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
}
public void Dispose() =>
Console.ForegroundColor = previous;
}
----

View File

@ -0,0 +1,30 @@
== How to fix it in MSTest
Remove the `ExpectedException` attribute in favor of using the https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.assert.throwsexception[Assert.ThrowsException] assertion.
=== Code examples
==== Noncompliant code example
[source,csharp,diff-id=1,diff-type=noncompliant]
----
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))] // Noncompliant
public void Method_NullParam()
{
var sut = new MyService();
sut.Method(null);
}
----
==== Compliant solution
[source,csharp,diff-id=1,diff-type=compliant]
----
[TestMethod]
public void Method_NullParam()
{
var sut = new MyService();
Assert.ThrowsException<ArgumentNullException>(() => sut.Method(null));
}
----

View File

@ -0,0 +1,30 @@
== How to fix it in NUnit
Remove the `ExpectedException` attribute in favor of using the https://docs.nunit.org/articles/nunit/writing-tests/assertions/classic-assertions/Assert.Throws.html[Assert.Throws] assertion.
=== Code examples
==== Noncompliant code example
[source,csharp,diff-id=2,diff-type=noncompliant]
----
[Test]
[ExpectedException(typeof(ArgumentNullException))] // Noncompliant
public void Method_NullParam()
{
var sut = new MyService();
sut.Method(null);
}
----
==== Compliant solution
[source,csharp,diff-id=2,diff-type=compliant]
----
[Test]
public void Method_NullParam()
{
var sut = new MyService();
Assert.Throws<ArgumentNullException>(() => sut.Method(null));
}
----

View File

@ -1,52 +1,18 @@
include::../../../shared_content/dotnet/csharp_dictionary.adoc[]
:language: csharp
== Why is this an issue?
include::../description.adoc[]
=== Noncompliant code example
[source,csharp]
----
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))] // Noncompliant
public void TestNullArg()
{
//...
}
----
=== Compliant solution
[source,csharp]
----
[TestMethod]
public void TestNullArg()
{
bool callFailed = false;
try
{
//...
}
catch (ArgumentNullException)
{
callFailed = true;
}
Assert.IsTrue(callFailed, "Expected call to MyMethod to fail with ArgumentNullException");
}
----
or
[source,csharp]
----
[TestMethod]
public void TestNullArg()
{
Assert.ThrowsException<ArgumentNullException>(() => /*...*/);
}
----
include::../exceptions.adoc[]
include::./how-mstest.adoc[]
include::./how-nunit.adoc[]
include::../resources.adoc[]
ifdef::env-github,rspecator-view[]
'''

View File

@ -1,3 +1,3 @@
It should be clear to a casual reader what code a test is testing and what results are expected. Unfortunately, that's not usually the case with the `ExpectedException` attribute since an exception could be thrown from almost any line in the method.
This rule detects MSTest and NUnit `ExpectedException` attribute.
This rule detects MSTest and NUnit `ExpectedException` attribute.

View File

@ -1,3 +1,8 @@
=== Exceptions
This rule ignores one-line test methods, since it is obvious in such methods where the exception is expected to be thrown.
This rule ignores:
* single-line tests, since it is obvious in such methods where the exception is expected to be thrown
* tests when it tests control flow and assertion are present in either a `{keyword_catch}` or `{keyword_finally}` clause
include::{language}/flow-example.adoc[]

View File

@ -28,7 +28,7 @@
"sqKey": "S3431",
"scope": "Tests",
"defaultQualityProfiles": [
"Sonar way"
],
"quickfix": "unknown"
}

View File

@ -0,0 +1,8 @@
== Resources
=== Documentation
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.assert.throwsexception[Assert.ThrowsException Method]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute[ExpectedExceptionAttribute Class]
* NUnit - https://docs.nunit.org/articles/nunit/writing-tests/assertions/classic-assertions/Assert.Throws.html[Assert.Throws]
* NUnit - https://docs.nunit.org/2.4/exception.html[ExpectedExceptionAttribute]

View File

@ -0,0 +1,31 @@
[source,vbnet]
----
<TestMethod>
<ExpectedException(GetType(InvalidOperationException))>
Public Sub UsingTest()
Console.ForegroundColor = ConsoleColor.Black
Try
Using alert As New ConsoleAlert()
Assert.AreEqual(ConsoleColor.Red, Console.ForegroundColor)
Throw New InvalidOperationException()
End Using
Finally
Assert.AreEqual(ConsoleColor.Black, Console.ForegroundColor) ' The exception itself is not relevant for the test.
End Try
End Sub
Public NotInheritable Class ConsoleAlert
Implements IDisposable
Private ReadOnly previous As ConsoleColor
Public Sub New()
previous = Console.ForegroundColor
Console.ForegroundColor = ConsoleColor.Red
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Console.ForegroundColor = previous
End Sub
End Class
----

View File

@ -0,0 +1,28 @@
== How to fix it in MSTest
Remove the `ExpectedException` attribute in favor of using the https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.assert.throwsexception[Assert.ThrowsException] assertion.
=== Code examples
==== Noncompliant code example
[source,vbnet,diff-id=1,diff-type=noncompliant]
----
<TestMethod>
<ExpectedException(GetType(ArgumentNullException))> ' Noncompliant
Public Sub Method_NullParam()
Dim sut As New MyService()
sut.Method(Nothing)
End Sub
----
==== Compliant solution
[source,vbnet,diff-id=1,diff-type=compliant]
----
<TestMethod>
Public Sub Method_NullParam()
Dim sut As New MyService()
Assert.ThrowsException(Of ArgumentNullException)(Sub() sut.Method(Nothing))
End Sub
----

View File

@ -0,0 +1,28 @@
== How to fix it in NUnit
Remove the `ExpectedException` attribute in favor of using the https://docs.nunit.org/articles/nunit/writing-tests/assertions/classic-assertions/Assert.Throws.html[Assert.Throws] assertion.
=== Code examples
==== Noncompliant code example
[source,vbnet,diff-id=2,diff-type=noncompliant]
----
<Test>
<ExpectedException(GetType(ArgumentNullException))> ' Noncompliant
Public Sub Method_NullParam()
Dim sut As New MyService()
sut.Method(Nothing)
End Sub
----
==== Compliant solution
[source,vbnet,diff-id=2,diff-type=compliant]
----
<Test>
Public Sub Method_NullParam()
Dim sut As New MyService()
Assert.Throws(Of ArgumentNullException)(Sub() sut.Method(Nothing))
End Sub
----

View File

@ -1,46 +1,18 @@
include::../../../shared_content/dotnet/vbnet_dictionary.adoc[]
:language: vbnet
== Why is this an issue?
include::../description.adoc[]
=== Noncompliant code example
[source,vbnet]
----
<TestMethod>
<ExpectedException(GetType(ArgumentNullException))> ' Noncompliant
Public Sub TestNullArg()
'...
End Sub
----
=== Compliant solution
[source,vbnet]
----
<TestMethod>
Public Sub TestNullArg()
Dim CallFailed As Boolean = False
Try
' ...
Catch ex As Exception
CallFailed = true
End Try
Assert.IsTrue(CallFailed, "Expected call to MyMethod to fail with ArgumentNullException")
End Sub
----
or
[source,vbnet]
----
<TestMethod>
Public Sub TestNullArg()
Assert.ThrowsException(Of ArgumentNullException)(Sub() ... )
End Sub
----
include::../exceptions.adoc[]
include::./how-mstest.adoc[]
include::./how-nunit.adoc[]
include::../resources.adoc[]
ifdef::env-github,rspecator-view[]
'''
== Comments And Links

View File

@ -1,4 +1,6 @@
:keyword_null: null
:keyword_async: async
:keyword_catch: catch
:keyword_finally: finally
:concept_method: method
:typeparameter_TResult: <TResult>
:typeparameter_TResult: <TResult>

View File

@ -1,4 +1,6 @@
:keyword_null: Nothing
:keyword_async: Async
:keyword_catch: Catch
:keyword_finally: Finally
:concept_method: procedure
:typeparameter_TResult: (Of TResult)
:typeparameter_TResult: (Of TResult)