17

My test fails because of the message:

The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.

This is my Code:

const double MAX_DELTA = 0.01;
Qv_ges qv_ges_NE;
double Sum_Qv_ges_R_FL;
Qv_ges Qv_ges_Quer;

[SetUp]
public void init()
{
    qv_ges_NE = Din1946.Calc_Qv_ges_NE(205.7d);
    Sum_Qv_ges_R_FL = 15d + 15d + 15d + 15d + 15d + 10d + 10d + 10d + 10d + 10d + 10d + 10d;
    Qv_ges_Quer = Din1946.Calc_Qv_ges_Quer(qv_ges_NE, Sum_Qv_ges_R_FL);
}

public IEnumerable<TestCaseData> TestCases_A()
{
    yield return new TestCaseData(72.5, Qv_ges_Quer.FL.Value, MAX_DELTA);
    yield return new TestCaseData(169.17, Qv_ges_Quer.RL.Value, MAX_DELTA);
    yield return new TestCaseData(241.67, Qv_ges_Quer.NL.Value, MAX_DELTA);
    yield return new TestCaseData(314.17, Qv_ges_Quer.IL.Value, MAX_DELTA);
}

[TestCaseSource("TestCases_A")]
public void MethodA(double expected, double value, double latitude)
{
    Assert.AreEqual(expected, value, latitude);
}

I used only static testcase-methods, but now I need to access non staic data like the variable Qv_ges_Quer: Qv_ges_Quer.IL.Value, Qv_ges_Quer.FL.Value.... so I removed static

Question:
How can I use a non static testcase?
I also noticed by debugging that it doesn't enter the SetUp at first.

This is my old code which I want to reorganize, maybe you know another/better way then the way above:

public void MethodA()
{
    Qv_ges qv_ges_NE = Din1946.Calc_Qv_ges_NE(205.7d);

    double Sum_Qv_ges_R_FL = 15d + 15d + 15d + 15d + 15d + 10d + 10d + 10d + 10d + 10d + 10d + 10d;
    Qv_ges Qv_ges_Quer = Din1946.Calc_Qv_ges_Quer(qv_ges_NE, Sum_Qv_ges_R_FL);

    Assert.AreEqual(72.5, Qv_ges_Quer.FL.Value, MAX_DELTA);
    Assert.AreEqual(169.17, Qv_ges_Quer.RL.Value, MAX_DELTA);
    Assert.AreEqual(241.67, Qv_ges_Quer.NL.Value, MAX_DELTA);
    Assert.AreEqual(314.17, Qv_ges_Quer.IL.Value, MAX_DELTA);
}
Perazim
  • 1,501
  • 3
  • 19
  • 42

2 Answers2

21

‍♀️ Zombie response, but better late than never.

Another way to accomplish this is to have your test case data source return a function object that accepts the non-static members you need as its parameter(s). Then your test calls that to create the data that you wish NUnit could pass into you.

In your case, that looks something like:

private static IEnumerable<TestCaseData> GetTestDataA()
{
    yield return new TestCaseData(72.5,   new Func<Qv_ges, double>( qvGesQuer => qvGesQuer.FL.Value ), MAX_DELTA);
    yield return new TestCaseData(169.17, new Func<Qv_ges, double>( qvGesQuer => qvGesQuer.RL.Value ), MAX_DELTA);
    yield return new TestCaseData(241.67, new Func<Qv_ges, double>( qvGesQuer => qvGesQuer.NL.Value ), MAX_DELTA);
    yield return new TestCaseData(314.17, new Func<Qv_ges, double>( qvGesQuer => qvGesQuer.IL.Value ), MAX_DELTA);
}

[TestCaseSource( nameof(GetTestDataA) )]
public void MethodA( double expected, Func<Qv_ges, double> getValue, double latitude)
{ 
    Assert.AreEqual( expected, getValue( Qv_ges_Quer ), latitude );
}

If you need multiple parameters, add them to the functor's and lambda's parameters or consider passing in this instead. If you need multiple return values, make the function object return a tuple:

new Func<Qv_ges, (double, double)>( qvGesQuer => (qvGesQuer.RL.Value, qvGesQuer.IL.Value) )

A different approach is to pass in nameof() strings as the test params and use reflection to get the values of those parameters.

metal
  • 6,202
  • 1
  • 34
  • 49
11

By design, the method, property or field used by the TestCaseSourceAttribute must be static. This is intended to avoid the need to instantiate the fixture class at the time the tests are loaded. Your fixture is only instantiated when we start the run - in the case of the GUI, each time we start the run - and its lifetime is only as long as it takes to run the fixture.

In your case, you have appear to have discovered that you can use a static method. That's best, if possible.

The only way to use instance methods here is to use the constructor TestCaseSourceAttribute(Type sourceType) where sourceType implements IEnumerable and returns your test case data directly. If you use this, I recommend using a different class from your TestFixture. It's not absolutely necessary. If you use the same class, different instances will be created at load time and run time, which have no connection whatsoever with one another. Many developers end up getting confused by this and try to leave state behind at load time for use by the tests. That won't work.

Charlie
  • 12,928
  • 1
  • 27
  • 31
  • 1
    So I can't acces an object with TestCaseSource because its TestCaseSource is static? Sry, I dont understand that :/ – Perazim Sep 30 '16 at 11:20
  • 1
    I answered your question on the basis that you wanted the source data method to be non-static. That's not possible. Nothing prevents the test methods from being instance methods. – Charlie Oct 01 '16 at 16:34
  • 2
    We used to have lots of TestCaseSources which used the TestData property of the test fixture to locate the set of the test case files. Won't be easy to bring this logic out. This used to work mostly OK with NUnit2 (with an exception that unnecessary test case sources were called for tests not even intended for run in this session), and that's a much breaking change in migration to NUnit3. – hypersw Mar 23 '17 at 00:43
  • 2
    The worst of all is when TestCaseSource calls into a virtual method which is overridden in derived classes. For that, I do not immediately know what to do. – hypersw Mar 23 '17 at 18:24
  • See also my answer about using functors to accomplish this instead. – metal Feb 01 '19 at 15:56
  • @Charlie, what is the state of this? Would there be way to use non-static methods for cases where I want test data using something generated in the [SetUp] method? – Ceco Feb 25 '20 at 08:36
  • Not that I know of, but you should ask on GitHub so the current folks working on NUnit can give a definitive answer. – Charlie Feb 26 '20 at 15:39