0

Are there any options to pass data from non-static TestCaseSource or ValueSource?

I want to pass some data to tests from an injected dbContext so I can't use static sources.

The following code throws an error: "The sourceName specified on a ValueSourceAttribute must refer to a non-null static field, property or method."

[Parallelizable(ParallelScope.All)]
[FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
public class MyTests
{
    public MyTests()
    {
    }

    public IEnumerable ValueSource
    {
        get
        {
            yield return new TestCaseData("test1");
            yield return new TestCaseData("test2");
            // yield some data from DbContext 
        }
    }

    [Test]
    public void MyTest([ValueSource(nameof(ValueSource))] string name)
    {
        Console.WriteLine(name);

    }
}

That doesn't work either.

[Parallelizable(ParallelScope.All)]
public class MyTests
{
    private static List<TestCaseData> additional = new List<TestCaseData>();

    [OneTimeSetUp]
    public void OneTimeSetUp()
    {
        additional.Add(new TestCaseData("test3"));
    }

    public static IEnumerable<TestCaseData> TestCases
    {
        get
        {
            yield return new TestCaseData("test1");
            yield return new TestCaseData("test2");
            
            foreach (var testCase in additional)
                yield return testCase;
        }
    }

    [Test, TestCaseSource(nameof(TestCases))]
    public void MyTest(string name)
    {
        Console.WriteLine(name);

    }
}
Sk1773r
  • 1
  • 1

1 Answers1

0

No.

However, you can work around the problem by using the [OneTimeSetUp] attribute to populate your static test data from the database. This will perform better, anyway, since you won't need to hit the database for each test.

[Parallelizable(ParallelScope.All)]
public class MyTests
{
    private static List<TestCaseData> databaseTests = new List<TestCaseData>();

    [OneTimeSetUp]
    public void OneTimeSetUp()
    {
        var dbData = // Run query...
        foreach (var item in dbData)
        {
            databaseTests.Add(TestCaseData(item));
        }
    }

    public static IEnumerable ValueSource
    {
        get
        {
            yield return new TestCaseData("test1");
            yield return new TestCaseData("test2");
            // yield some data from DbContext
            foreach (var testCase in databaseTests)
                yield return testCase;
        }
    }

    [Test]
    public void MyTest([ValueSource(nameof(ValueSource))] string name)
    {
        Console.WriteLine(name);

    }
}

NOTE: It is usually not advisable to use a real database when testing. This means your tests will require a database to be present in the environment that you run them in, instead of having tests that "just work" for whoever runs them. Instead, it is preferable to mock the data that you would ordinarily retrieve from a database so you don't have this extra dependency.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • It also throws the same error. The problem is that NUnit requires the TestCaseSource or the ValueSource to be static. – Sk1773r May 23 '22 at 16:15
  • Oops, I changed it to be `static`. – NightOwl888 May 23 '22 at 16:16
  • That doesn't work either. It just skips cases from SetUp. I've written an example below – Sk1773r May 23 '22 at 16:32
  • Are the test cases being populated when you set a breakpoint in `OneTimeSetUp`? – NightOwl888 May 23 '22 at 16:35
  • Yes, it is populated, but the source defines before the SetUp. I've found an answer :( https://github.com/nunit/nunit/issues/141 – Sk1773r May 23 '22 at 16:43
  • Do you have any other ideas? I think about the ServiceLocator to resolve services in the static method but I don't like it... – Sk1773r May 23 '22 at 17:00
  • The only option I can think of is to copy the code from [`TestCaseSourceAttribute`](https://github.com/nunit/nunit/blob/v3.13.3/src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs) and use a different version of [`BuildFrom`](https://github.com/nunit/nunit/blob/v3.13.3/src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs#L133) that loads your test cases from the database. NUnit uses interfaces to detect the presence of the attributes, so as long as you use the interfaces, it should still work. – NightOwl888 May 23 '22 at 17:13
  • Use of static is a side issue. Your fundamental problem is that all the test cases must be defined before the tests start to run. That means before OneTimeSetUp as well. BTW, I'm commenting but not answering because there's not enough info in the question. Code showing where you get the dbcontext and what you actually do with it would help. – Charlie May 23 '22 at 18:04
  • Also, if you thoroughly review the docs for TestCaseSourceAttribute, you'll see that there is a format, which allows non-static data. But that won't solve your problem by itself. It's the order of initialization that causes the problem. – Charlie May 23 '22 at 18:05
  • I can write some extended example and publish it on GitHub. It will take some time. In my tests I build the request model, send it to API service and check everything is ok. When I build the request model, I need to set some properties for it. To get these properties' values, I need to execute some actions using injected services. For example: send an another request and get the property value from a response or execute a SQL query and more. So, I really want to write the generic test method, that will receive a prepared property values to build a model and work with it. – Sk1773r May 24 '22 at 07:39