Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NUnit1025 should ignore abstract classes #696

Open
NinjaCross opened this issue Feb 27, 2024 · 3 comments
Open

NUnit1025 should ignore abstract classes #696

NinjaCross opened this issue Feb 27, 2024 · 3 comments

Comments

@NinjaCross
Copy link

When creating complex tests, I try as much as possible to adopt OOP and general coding best practices.
An example, is creating an abstract test class, from which many concrete classes inherit and define their own extensions and specializations.
If the base abstract class contains a test method using the ValueSource attribute, the value of ValueSource shouldn't be checked, or its absence shouldn't be considered an error (maybe just a warning ?), because the method could be specified into an inherited class.
An example:

public class MyTestScenario
{
 // some class containg the data of the scenario being tested
}

//------------------------------------------------------------------

[TestFixture]
public abstract MyBaseTests
{

 // This would trigger NUnit1025 because "GetTestScenarios" is only defined as a static method into the inherited classes.
 [Test]
 public void MyTest([ValueSource("GetTestScenarios")] MyTestScenario scenario) 
 {
    // test logics, that can also invoke overridable members inherited classes

    DoSomething1(scenario);
    DoSomething2(scenario);
 }

 protected abstract DoSomething1(MyTestScenario scenario);  // must be implemented in inherited classes

 protected virtual DoSomething2(MyTestScenario scenario)
 {
  // blah blah
 }

}

//------------------------------------------------------------------

public sealed Scenario1Tests : MyBaseTests // inherits from MyBaseTests, but define its own test scenarios
{
 private static IEnumerable<MyTestScenario> GetTestScenarios()
 { 
   // return the tests scenarios for the "Scenario1Tests" class
 }

protected override DoSomething1(MyTestScenario scenario)
{
  // blah blah
}

 protected override DoSomething2(MyTestScenario scenario)
 {
  // blah blah
 }
}

//------------------------------------------------------------------

public sealed Scenario2Tests : MyBaseTests // inherits from MyBaseTests, but define its own test scenarios
{
 private static IEnumerable<MyTestScenario> GetTestScenarios()
 { 
   // return the tests scenarios for the "Scenario2Tests" class
 }

 protected override DoSomething1(MyTestScenario scenario)
 {
   // blah blah
 }
}

//------------------------------------------------------------------

public sealed Scenario3Tests : MyBaseTests // inherits from MyBaseTests, but define its own test scenarios
{
 private static IEnumerable<MyTestScenario> GetTestScenarios()
 { 
   // return the tests scenarios for the "Scenario3Tests" class
 }

 protected override DoSomething1(MyTestScenario scenario)
 {
   // blah blah
 }
}

Is my suggestion worthy, or is there a better approach that can avoid raising NUnit1025 and implement the tests in a OOP/structured way ?

@manfred-brands
Copy link
Member

@NinjaCross I see your problem and will try to see if I can come up with a design where it works without a warning.

The reason the warning pops up is that there is no guarantee or compile error if a derived class does not implement the needed method.

Can you tell me what are the .Net frameworks you are targeting. If .net 6 and later there might be more options.

@NinjaCross
Copy link
Author

NinjaCross commented Feb 27, 2024

@NinjaCross I see your problem and will try to see if I can come up with a design where it works without a warning.

Great to know @manfred-brands, thanks !

The reason the warning pops up is that there is no guarantee or compile error if a derived class does not implement the needed method.

Yes, I understand, the motivation is absolutely reasonable

Can you tell me what are the .Net frameworks you are targeting. If .net 6 and later there might be more options.

.NET 6 or greater

IMHO the best option would consist in being able to refer to a virtual, non-static method, but now it's not possibile, and I can understand how that would complicate things greately inside the NUnit engine.
A pity, indeed :(
That's why I asked if I was implementing my tests in the best way, and if there are alternative approaches allowing to use a clean, OOP-based, inheritance paradigm.

Thankyou anyway for any improvement you could implement :)

@manfred-brands
Copy link
Member

I had hoped that the C#11 static abstract interface members would work, but if the baseclass claims to implement the interface it needs that static method.

I tried abstract class MyBaseTest<T> where T : IGetTestScenarios and then use that like in:
[ValueSource(typeof(T), typeof(T.GetTestScenarios))].
Unfortunately C# doesn't allow specifying typeof(T) as a type parameter in an attribute.

One other option is to drop your inheritance and only have a (static) class with tests:

public sealed class Scenario1Tests
{
    private static IEnumerable<MyTestScenario> GetTestScenarios()
    {
        // return the tests scenarios for the "Scenario1Tests" class
        return Enumerable.Empty<MyTestScenario>();
    }

    [TestCaseSource(nameof(GetTestScenarios))]
    public void DoSomething1(MyTestScenario scenario)
    {
        // blah blah
    }

    [TestCaseSource(nameof(GetTestScenarios))]
    public void DoSomething2(MyTestScenario scenario)
    {
        MyBaseTests.DoSomething2(scenario);
    }
}

If you want enforce an DoSomething1 test, you can have an interface that the scenario tests derive from.

I looked at the nunit code and do think that it is possible to actually use instance members instead of static

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

No branches or pull requests

2 participants