Mock objects on JavaScript

Many frameworks use the word "mock" for what in reality are "spies". But that's OK, the work "mock" in English covers actually all the possible test double types...

However according to G. Meszaros, a mock object is a specific type of test double that is configured with expectations before exercising the system under test. During the execution the mock compares the calls being made, to its expectations failing if they don't match.

The two frameworks I use for test doubles in JavaScript are Jasmine and Sinon.js. And I also use some helpers of my own. None of these two frameworks provide actual mock objects. Sinon has a "mock" function that let us configure expectations before exercising the system under test. It behaves like a mock if we are working with functions only. Nonetheless if we are working with objects (OOP), then what Sinon provides is not a mock object because a call to any other method in the object other than the expected, will not fail the test. And it should as unexpected calls should make the test fail.

The solution to have real mock objects with Sinon is just a little helper:

  2. //------ HELPER FUNCTIONS
  3. function unexpectedCall(functName)
  4. {
  5. return function(){
  6. throw new Error('Function ' + functName + ' was not expected but invoked');
  7. };
  8. }
  9. function inocuousCall(){
  10. return function(){};
  11. }
  12. function replaceAll(obj){
  13. return {
  14. methodsWith: function(fn){
  15. for (var propName in obj)
  16. if (typeof(obj[propName]) == 'function')
  17. obj[propName] = fn(propName);
  18. }
  19. }
  20. }
  21. function stubThe(obj){
  22. replaceAll(obj).methodsWith(inocuousCall);
  23. }
  24. function mockThe(obj){
  25. replaceAll(obj).methodsWith(unexpectedCall);
  26. }
  28. //------ THE TEST
  30. it("stores the user when adding a new one", function(){
  31. // the DOC (depended-on component)
  32. var repo = userRepository();
  33. // the SUT (system under test) and dependency injection
  34. var service = userService(repo);
  35. // patching the DOC, making it a mock
  36. mockThe(repo);
  37. var mock = sinon.mock(repo);
  38. // configuring expectation:
  39. mock.expects("store").once();
  41. // exercising the SUT
  42. service.add(new User({email: ''}));
  44. // verifying configured expectations
  45. mock.verify();
  46. });

In languages like Java or C#, test doubles frameworks create a replacement for all the methods in the target class/interface. In JavaScript however, frameworks replace single functions. For this reason it's possible to have the same object acting as the SUT and the double in a single test. But the fact it's possible doesn't mean it's right. Whenever you find yourself in a test using a double for some method and then invoking another method in the same object, think carefully whether the design is appropriate. That smell is usually pointing out that you should extract a new class and create a dependency between the old and the new, so that one acts as the double and the other as the SUT.

Enjoyed reading this post?
Subscribe to the RSS feed and have all new posts delivered straight to you.
  • JJ Martínez

    Great! But does it make sense to mock an instance of an object? Maybe it’s because I’m a C# developer but I can’t find it.

    Anyway, have u tried MockusJS? Example:

    var myMock = Mockus.Mock(UserRepository);
    myMock.Expects.Store.ToBeCalled(X); -> X = times
    myMock.Expects.Store.WithParams(It.IsAnyFunction(), “myValue”, 1); –> Uses strict mode

    Also combined with InjectusJS looks great 😛

    var userService = Injectus.Resolve(UserService); <– It resolves the dependency with UserRepository and all required dependencies

    Thnx && Regards!

  • Carlos Ble

    Thanks for the information, I didn’t know about this framework. It doesn’t look very idiomatic (js) but looks powerful.
    In dynamic languages I find that IoC containers are not necessary. And that has to do with mocking instances 🙂 It doesn’t really matter in JavaScript whether your are mocking a particular instance or the whole “class”. Well, if the second produces side effects among tests then it’s even bad idea.