I was involved in writing an application which involved pairing participants up for activities. One thing we had to limit was that every participant was paired with 4 other participants. That way the burden would be even on all participants and the distribution of activities would be fair.
Another aspect of this story was to make sure we could do this repeatedly and ensure that a participant is not paired with the same one in the next cycle.(We wanted to come up with a way to do this automatically every so often.)
When we went ahead and analysed this story we found out that, pairing similar to this was being done manually where someone would pick names and assign them. Fairness was verified manually.
This process of throwing pieces of paper and picking names worked for small groups and it became a nightmare once we wanted to do this multiple times. Often we would come across repeated pairs and confusion about what is fair and what is not. The randomness of the whole thing was arbitrary and error prone.
Having known all that we starting designing this story. Figuring out the tests was our next task. We started writing tests but all we ended up with was something that said "test_random_numbers_are_generated" and the body of the test could only go about white box testing the fact that the randomization method was called. Writing different variations gave us something we already knew about. We also went about testing the math behind randomization - which we dropped after initial attempts - since it was clearly testing implementation.
Intrigued with the idea of using random numbers the inherent difficulty of testing random numbers Paulo(who I was working with then) asked how could we test randomization - its almost untestable - he said.
Stepping back and looking at the problem from a the business requirement looking at the desired behavior we started rethinking about our tests and moving away from testing random numbers. What we found was the application as such never cared about randomization. All that the administration was interested in was with the fairness of the pairing. Another thing that was implicit in this requirement was that a person cannot be paired with self. Randomness was our implementation detail and it was how we were thinking about implementing this. Also we did not even need the random numbers provided by ruby. All we needed was
parity = 2
set = [1,2,3,4,5,6,7,8,9,10]
relations = Relation.associate_with_parity(set, parity)
relations.each do |index, value|
assert_equal parity, value.size
assert_equal false, value.include?(index)
In both these tests the acceptance criteria do not care about randomness of the result. Also it does not matter how this randomness if at all is implemented. What matters is the fairness of the pairing and that there is no one who pairs with self.
In summary I would say that if you are thinking about testing randomness - your application may need another look.
Try asking the question - Does my domain care about the randomness(implementation)? - More often than not your answer would be NO and you will be able to figure out better functional tests for your application. Once you start thinking in terms of behavior of the system instead of the implementation, you will end up with better tests.