Mocking calls to random number generators is a useful and important technique. Firstly it gives you a way into testing something that should operate randomly in production and because random number generation comes from built-in or system libraries normally it is also a measure of how well your mocking library actually works.
For Ruby I tend to use RSpec and its in-built mocking. Here the mocking is simple, of the form (depending on whether you are expecting or stubbing the interaction):
However what is tricky is determining what the receiver should be. In Ruby random numbers are generated by Kernel so rand is Kernel.rand. This means that if the mocked call occurs in a class then the class is the receiver of the rand call. If the mocked call is in a module though the receiver is properly the Kernel module.
So in summary:
For a class: MyClass.should_receive(:rand) For a module: Kernel.should_receive(:rand)
This is probably obvious if you a Ruby cognoscenti but is actually confusing compared to other languages.
In Python random functions are provided by a module, which is unambiguous but when using Mock I still had some difficultly as to how I set the expectation. Mock uses strings for the method called by the instance of the item for the mock anchor. This is how I got my test for shuffling working in the end.
from mock import Mock, patch_object import random mock = Mock() class MyTest(unittest.TestCase): @patch_object(random, 'shuffle', mock) def test_shuffling(self): thing_that_should_shuffle() self.assertTrue(mock.called, 'Shuffle not called')
You can see the same code as a technicolor Gist.
This does the job and the decorator is a nice way of setting out the expectation for the test but it was confusing as to whether I am patching or patch_object’ing and wouldn’t it be nice if that mock variable could be localised (maybe it is and I haven’t figured it out yet).
Next time I’m starting some Python code I am going to give mocktest a go. It promises to combine in-built mock verification which should simplify things. It would be nice to try and combine it with Hamcrest matchers to get good test failure messages and assertions too.