Java – Testing a function that uses random number generator

javarandomunit testing

I have a method which is a part of the interface I am implementing. This method calls another private method which uses a random number generator to produce output and returns it to the calling method. I want to test the calling method. How can I do that?
This is the method under test:

 @Override
 public String generate(int wordCount) {
    StringBuilder sentence = new StringBuilder();

    List<String> selectedStrings = selectRandomStringsFromInternalVocabulary(wordCount, new Random());
    selectedStrings.sort(Comparator.<String>naturalOrder());

    swapOddIndexedStringsWithEvenIndexedStrings(selectedStrings);

    for (String word: selectedStrings)
        sentence.append(word)
                .append(" ");


    return sentence.toString().trim();
}

This is the method that uses random number generator:

private List<String> selectRandomStringsFromInternalVocabulary(int wordCount, Random random) {
    List<String> selectedStrings = new ArrayList<>();
    int wordCountInVocabulary = internalVocabulary.size();

    while (wordCount-- != 0) {
        int stringIndex = random.nextInt(wordCountInVocabulary);
        selectedStrings.add(internalVocabulary.get(stringIndex));
    }

    return selectedStrings;
}

There are a few things that I've thought I can do:
1. Make the second method package-private and test it. But I don't want to test a private method if I can avoid it.
2. Add Random as a parameter to the calling function and pass a mock during test. However, its part of the interface and other classes implementing it does not use RNG. Furthermore, I don't want clients to know about the implementation details.

I have gone through these questions:
1. Unit testing methods with indeterminate output
2. Unit Testing a function with random behavior

But the suggestions are similar to what I mentioned above.

Best Answer

The question you should be asking yourself is "What am I trying to prove?" (NOTE: "prove" is not used in the mathematical sense, but simply to test the validity of something). Unit tests are there to test that your application is functioning within it's designed parameters. Different tests for correctness require different approaches:

  • You can verify that all words in the returned the string exist in the global vocabulary
  • You can verify that there are, or are no repeats of words in that returned string
  • You can verify that there is no extraneous white space at the beginning and end
  • You can verify that the number of words enforces positive numbers
  • You can verify that the a list of 0 words provides an empty string

That might be sufficient for your needs. Those types of tests are also fairly robust. They won't break if the implementation of the random number generator changes. You are still working through the external interface to get at the internal features.

I think in this case it would prove both brittle and only marginally useful to test specific sequences of strings. There aren't really that many branch paths to worry about in your example. Think about the contracts and what it is you really need to ensure, and embrace the randomness.


The important thing to note is that you are not proving randomness. You are ensuring that your method behaves as expected.

If you were testing randomness, you would need to do some statistical analysis to prove the spread of random numbers was as expected, etc. Those kinds of tests would require "proof" in the mathematical sense of the word. Unit tests aren't the right tool for that job.

Related Topic