TestCaseSource in N-Unit

3.4k views Asked by At

I'm writing some N-Unit tests and I'm having a little difficulty. I'm trying to wire up a Test to a TestCaseSource in my code, but it doesn't seem to be constructing the objects correctly.

Here is my Test Method:

    [Test, TestCaseSource(typeof(TestCaseStuff), "GetTestCases", Category = "Release")]
    public void WithdrawMoney(TestCaseStuff tc)
    {
        double newBalance = AccountBalance - tc.amt;
        if (newBalance < 0)
        {
            Assert.Fail(String.Format("You can't withdraw {0}! You've maxed" +
                "out your bank account.", tc.amt.ToString("C2")));
        }

        AccountBalance = newBalance;
        Assert.Pass(String.Format("Your new balance is {0}.", 
        (AccountBalance).ToString("C2")));
    }

And my TestCaseSource:

public class TestCaseStuff
{
    public double amt { get; set; }

    public TestCaseStuff()
    {
        Random rnd = new Random();
        this.amt = rnd.Next(0, 20);
    }

    [Category("Release")]
    public IEnumerable<TestCaseData> GetTestCases()
    {
        for (int i = 0; i < 500; i++)
        {
            yield return new TestCaseData(this);
        }
    }
}

The is mostly serving as proof of concept, so that when it comes time to write an actual test with complex objects, I'll know that I can just write a loop like the one above and put random values in my object. However, every instance of TestCaseStuff that is returned to my test method is the same.

Update:

The answer below is correct. When I was passing it into N-Units TestCaseData object, I assumed (incorrectly) that it would simply pass that instance in by value. Apparently, it is done by reference which is why the value was always the same.

On top of that, I was using the Random class incorrectly. It isn't something I deal with usually, but I failed to read up on it properly. As explained in the link below, when using Random with it's default constructor, the seed value is derived from the system clock. As a result, if you instantiate several Random objects in quick succession they will share the same default seed value and produce the same values.

So, as a result of these developments, my code now reads:

public class TestCaseStuff
{
    public double amt { get; set; }
    private static readonly Random rnd = new Random();

    public TestCaseStuff()
    {
        this.amt = rnd.Next(0, 20);
    }

    [Category("Release")]
    public IEnumerable<TestCaseData> GetTestCases()
    {
        for (int i = 0; i < 500; i++)
        {
            yield return new TestCaseData(new TestCaseStuff());
        }
    }

}
2

There are 2 answers

1
Daisuke Shimamoto On BEST ANSWER

The above code will only instantiate one copy of TestCaseSource and keep on feeding the same value.

If the intention is to generate a random number in every loop, I think you should make a static instance of the Random object and generate a number from it every time you create a new TestCaseStuff object.

I would rewrite the TestCaseStuff like this:

public class TestCaseStuff
{
    // Static instance of a random number generator.
    private static readonly Random RNG = new Random();

    public double amt { get; set; }

    public TestCaseStuff()
    {
        this.amt = RNG.Next(0, 20);
    }

    [Category("Release")]
    public IEnumerable<TestCaseData> GetTestCases()
    {
        for (int i = 0; i < 500; i++)
        {
            // NOTE: You need to create a new instance here.
            yield return new TestCaseData(new TestCaseStuff());
        }
    }
}
1
Magnus Gren On

You are creating a new Random instance every time you call :

yield return new TestCaseData(this);

Try moving it outside the loop and you should start getting random values.

Found this post explaining it well! How do I seed a random class to avoid getting duplicate random values