views:

66

answers:

4

I'm trying to write a unit test for a class that generates distinct strings. My initial reaction was the following:

public void GeneratedStringsShouldBeDistinct()
{
    UniqueStringCreator stringCreator = new UniqueStringCreator();
    HashSet<string> generatedStrings = new HashSet<string>();
    string str;

    for (int i = 0; i < 10000; i++)
    {
        str = stringCreator.GetNext();
        if (!generatedStrings.Add(str))
        {
            Assert.Fail("Generated {0} twice", str);
        }
    }
}

I liked this approach because I knew the underlying algorithm wasn't using any randomness so I'm not in a situation where it might fail one time but succeed the next - but that could be swapped out underneath by someone in the future. OTOH, testing of any randomized algorithm would cause that type of test inconsistency, so why not do it this way?

Should I just get 2 elements out and check distinctness (using a 0/1/Many philosophy)?

Any other opinions or suggestions?

+1  A: 

I would keep using your approach; it's probably the most reliable option.

By the way, you don't need the if statement:

Assert.IsTrue(generatedStrings.Add(str), "Generated {0} twice", str);
SLaks
A: 

If I wanted to test code that relied on random input, I would try to stub out the random generation (say, ITestableRandomGenerator) so that it could be mocked for testing. You can then inject different 'random' sequences that appropriately trigger the different execution pathways of your code under test and guarantee the necessary code coverage.

The particular test you've shown is basically a black box test, as you're just generating outputs and verifying that it works for at least N cycles. Since the code does not have any inputs, this is a reasonable test, though it might be better if you know what different conditions may impact your algorithm so that you can test those particular inputs. This may mean somehow running the algorithm with different 'seed' values, choosing the seeds so that it will exercise the code in different ways.

Dan Bryant
A: 

If you passed the algorithm into UniqueStringCreator's constructor, you could use a stub object in your unit testing to generate psuedo-random (predictable) data. See also the strategy pattern.

DanDan
A: 

If there's some kind of check inside the class, you can always separate out the part which checks for distinctness with the part that generates the strings.

Then you mock out the checker, and test the behaviour in each of the two contexts; the one in which the checker thinks a string has been created, and the one in which it doesn't.

You may find similar ways to split up the responsibilities, whatever the underlying implementation logic.

Otherwise, I agree with SLaks - stick with what you have. The main reason for having tests is so that the code stays easy to change, so as long as people can read it and think, "Oh, that's what it's doing!" you're probably good.

Lunivore