Skip to content

Conversation

@stevenaw
Copy link
Member

Fixes #957

@stevenaw
Copy link
Member Author

@manfred-brands @mikkelbu I've made this as a draft already as it's most of the way there, however I have very little experience with the analyzers and am encountering an issue.

There's a failing test, NoWarningWhenNumberOfParametersOfTestWillFitIntoParamsArrayForGenericMethod (which I've added) to account for a params argument of type T in a generic method. There is some validation in place already to check for type assignments to validate NUnit1018 however I don't know if that's relevant for the generic case. My complication is that I'm unsure how to determine if the type of the params array is a generic type in order to opt out of the type checking there. I was hoping I could ask for a hint in the direction to go to work past that

@stevenaw
Copy link
Member Author

Ok I think I'm making a bit more headway here. I think I've moved past my issue and I'll continue chipping away at this.

@manfred-brands
Copy link
Member

Ok I think I'm making a bit more headway here. I think I've moved past my issue and I'll continue chipping away at this.

Just when I was going to have a look, now I can just sit and wait.

@stevenaw stevenaw force-pushed the testcasesource-params branch 2 times, most recently from a2f6b2d to 4955308 Compare November 23, 2025 19:05
No testcasesource warnings for optional or params are present
@stevenaw stevenaw force-pushed the testcasesource-params branch from 4955308 to eb23a58 Compare November 23, 2025 19:08
@stevenaw stevenaw marked this pull request as ready for review November 23, 2025 19:26
@stevenaw
Copy link
Member Author

I think this is ready for review. I ended up avoiding generic method scenarios since it looks like the existing code also option to avoid explicit type inference + validation for those cases

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the NUnit1029 analyzer to properly handle TestCaseSource usage with params arrays and optional parameters. The analyzer now correctly accounts for methods that can accept a variable number of arguments through params or optional parameters, preventing false positive warnings.

Key Changes:

  • Modified parameter count validation to include params parameters in the analysis
  • Restructured the validation logic to allow methods with optional or params parameters to accept TestCaseSource data
  • Added comprehensive test coverage for params arrays, optional parameters, and generic methods

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
TestCaseSourceUsesStringAnalyzer.cs Updated parameter counting logic to include params parameters and restructured validation to handle optional/params scenarios
TestCaseSourceUsesStringAnalyzerTests.cs Added four new test cases covering params arrays, generic methods, and optional parameters

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 614 to 620
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
}

[TestCaseSource(↓nameof(TestData))]
public void ShortName(int a = 1, int b = 2)
{
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable 'x' does not exist in this scope. The parameters are named 'a' and 'b', not 'x'.

Suggested change
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
}
[TestCaseSource(nameof(TestData))]
public void ShortName(int a = 1, int b = 2)
{
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
Assert.That(a, Is.GreaterThanOrEqualTo(0));
Assert.That(b, Is.GreaterThanOrEqualTo(0));
}
[TestCaseSource(nameof(TestData))]
public void ShortName(int a = 1, int b = 2)
{
Assert.That(a, Is.GreaterThanOrEqualTo(0));
Assert.That(b, Is.GreaterThanOrEqualTo(0));

Copilot uses AI. Check for mistakes.
Comment on lines 614 to 620
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
}

[TestCaseSource(↓nameof(TestData))]
public void ShortName(int a = 1, int b = 2)
{
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable 'x' does not exist in this scope. The parameters are named 'a' and 'b', not 'x'.

Suggested change
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
}
[TestCaseSource(nameof(TestData))]
public void ShortName(int a = 1, int b = 2)
{
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
Assert.Pass();
}
[TestCaseSource(nameof(TestData))]
public void ShortName(int a = 1, int b = 2)
{
Assert.Pass();

Copilot uses AI. Check for mistakes.
Copy link
Member

@manfred-brands manfred-brands left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some small remarks on the use of RoslynAssert.
Your example test code didn't get to the analyzer as there were compile errors.

I pushed the necessary fixes up, but the underlying test code doesn't work when run in NUnit 4.4.0.
I'm trying it now on master and see if it is related to the bug you raised

[TestCaseSource(↓nameof(TestData))]
public void ShortName(int a, int b = 2)
{
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this compiles during testing as there is no x variable.

Comment on lines 617 to 621
[TestCaseSource(↓nameof(TestData))]
public void ShortName(int a = 1, int b = 2)
{
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate function with same name and parameters. should also not compile.

}
}", additionalUsings: "using System.Collections.Generic;");

RoslynAssert.NoAnalyzerDiagnostics(analyzer, testCode);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might explain it.
I thought we always used RoslynAssert.Valid to ensure the code is correct.

[TestFixture]
public class NoWarningWhenNumberOfParametersOfTestWillFitIntoOptionalParams
{
[TestCaseSource(↓nameof(TestData))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the code is valid, there should be no character which otherwise indicates the expect error location.

Comment on lines 583 to 599
[TestFixture]
public class NoWarningWhenNumberOfParametersOfTestWillFitIntoParamsArrayForGenericMethod
{
[TestCaseSource(↓nameof(TestData))]
public void ShortName<T>(params T[] x)
{
Assert.That(x.Length, Is.GreaterThanOrEqualTo(0));
}

static IEnumerable<int> TestData()
{
for (int i = 1; i <= 3; i++)
{
yield return i;
}
}
}", additionalUsings: "using System.Collections.Generic;");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NUnit itself fails to run this test with Unable to determine type arguments for method.

I always extract the code from the string to see if NUnit will actually run it.

[TestCaseSource(↓nameof(TestData))]
public static void TestLotsOfNonRequiredParams(params int[] z)
{
Assert.That(z, Is.Not.Default);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default does not exist in NUnit 3.14. The analyzer test run against both 3.14 and 4.4
Also Default for an array is the same as Null, but a params array should never be null.

@stevenaw
Copy link
Member Author

Thanks @manfred-brands ! Appreciate your fixes here. I mentioned that I am new on the analyzer side.

I thought we always used RoslynAssert.Valid to ensure the code is correct.

I hadn't realized this was the case but I can see now why we would want to. I suppose my using NoAnalyzerDiagnostics would also mask when no diagnostics could be raised due to compilation issues 🤦‍♂️
I'll pull your changes and push another quick update. The original inclusion of the character was copied from an existing test. I'll quickly remove the remaining places the file uses it to avoid future copy/paste issues.

@manfred-brands
Copy link
Member

manfred-brands commented Nov 24, 2025

I'll quickly remove the remaining places the file uses it to avoid future copy/paste issues.

No. Only the tests that have RoslynAssert.Valid.
The ones that expect a diagnostic use the location of the marker to match line numbers.

Please revert your last commit!

@stevenaw stevenaw force-pushed the testcasesource-params branch from 23522b6 to 3028d64 Compare November 24, 2025 02:45
@stevenaw
Copy link
Member Author

Done. Sorry about that, thanks for the clarification

@stevenaw
Copy link
Member Author

stevenaw commented Dec 7, 2025

@manfred-brands Thanks for the review here.
I understand we're probably waiting to merge and release this until after the framework side goes out in v4.5 of the framework. While we are waiting is there anything else you'd like to see here? Alternately, were you waiting to approve until after the framework side goes stable?

{
// more than one required parameter is always a mismatch
// zero parameters of any kind is also a mismatch
context.ReportDiagnostic(Diagnostic.Create(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a new test case that validates this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @manfred-brands
Do you mean this comment?

// more than one required parameter is always a mismatch
// zero parameters of any kind is also a mismatch

That comment was added by me to clarify the existing logic for myself prior to and after refactoring the logic a bit. I had thought they're already covered through existing tests here:

Unless, would you prefer additional cases with 0 and two required args which also include optional args in the signature like this?

[TestCaseSource(nameof(TestData))]
        public void ShortName(int x, int y, int z = 2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Relax NUnit1029 for TestCaseSource where method accepts a single "params" array

2 participants