Search

  1. MockJS - Mockito syntax for JavaScript Unit Testing

    A couple of weeks ago I’ve learnt  about Mockito.
    (yes, I’ve been hiding under a rock all this time)

    Like most developers, I instantly found that Mockito’s API syntax makes it really easy to learn and use mock objects.

    I’ve looked around for an equivalent in JavaScript but found nothing suitable…
    Most solutions rely on unfriendly syntaxes or strange dependencies and, in my view, none was able to implement JavaScript mock objects in a simple and reliable way.

    So, as a part-time project, I developed an alternative Mock object library in JavaScript.
    I ported the raw concepts of Mockito, thrown in the JavaScript specifics, added a fresh perspective and some ECMA5 magic, mixed everything up in a repo and came up with something new.

    The resulting project is MockJS and can be followed at https://github.com/badoo/MockJS

    More and more I start developing APIs by developing the use case first, and only later the API core itself - a kind of API Driven Development if you will. I found it to generate the best usability for the end developer and very clear achievements while developing the API.

    So for MockJS I started with a very simple goal - use Mockito syntax to mock the following object:

    var myObject = { 
        foo:function(str){ 
            alert(str); 
            return str; 
        } 
    }
    



    I started with the Mockito original stubbing syntax:

    var mock = Mock(myObject); 
    when( mock.foo() ).thenReturn(true); 
    
    mock.foo(); //returns true
    



    The next step was to match arguments.
    I took advantage of how easy it is using callbacks in JavaScript and took care of wrapping the default callbacks in a nice Match object to avoid possible conflicts in the global scope.

    So I hooked a few default argument matchers:

    var mock = Mock(myObject); 
    when( mock.foo(Match.anyString) ).thenReturn(true); 
    
    mock.foo("yay!"); //returns true
    



    As an added bonus, because matchers are just simple callbacks, it allows the developer to easily create their own custom argument matchers:

    var mock = Mock(myObject); 
    var positive = function(arg){ 
        return arg>0; 
    }
    
    when( mock.foo(positive) ).thenReturn(true); 
    
    mock.foo(6); //returns true
    



    OK, now we need to be able to return a result based on the matched arguments, or even an argument itself - stubbing with callbacks is very simple in JavaScript:

    var mock = Mock(myObject); 
    
    when( mock.foo(Match.anyString) ).thenReturn(function(str){ return str; }); 
    
    mock.foo("yay!") //returns "yay!"
    



    To wrap up the Mockito basics, we need to verify behaviours:

    var mock = Mock(myObject); 
    mock.foo(); //returns undefined (default) 
    
    verify( Acts.once, mock.foo() );
    



    Great!
    We actually improved the original Mockito syntax a bit by making it even simpler!
    Nice.

    Wait, what about functions?
    In javascript a function does not have to be a method of a class instance… it can be just a variable in the current scope..
    Oh Crap! 

    Let’s try to mock functions too:

    function answer(question){ 
        return someAnswer; 
    } 
    
    var mockAnswer = Mock(answer); 
    when( mockAnswer(Match.everything) ).thenReturn(42); // :) 
    
    mockAnswer("what?"); //returns 42 
    
    verify( Acts.once, mockAnswer(Match.everything) );
    



    Nice!
    What other JavaScript specifics do we need? - right… context calls for our functions and methods.

    when( mockAnswer.apply(Match.anyContext, [Match.everything]) ).thenReturn(42); 
    
    when( mock.foo.call(myContext) ).thenReturn(true); 
    
    var myContext = {} 
    var myContext.foo = mock.foo; 
    myContext.foo(); //works!
    



    Awesome!
    Now we can mock anything!!

    Well, not anything.. what about jQuery?
    The $ wrapper itself we can easily mock, but how can we mock the returned DOM wrapper object without actually being able to access the generating class?

    So, the final piece of the puzzle was the ability to mock inaccessible objects.
    Initially it looked like a big problem, but looking at it from a simplistic point of view we only need to mock the object with the methods that will be used in our test case…

    So, we only really needed a simple syntax to create a custom mock object with a few methods of our choice:

    var kennyMock = Mock.new(['addClass', 'removeClass', 'remove']); 
    $ = Mock($); 
    
    when( $('#kenny') ).thenReturn(kennyMock); 
    when( kennyMock.remove() ).thenThrow("OMG! They killed Kenny!"); 
    
    var kenny = $('#kenny'); 
    kenny.addClass('headshot'); 
    kenny.remove(); //throws exception 



    Great! Now we have something really cool!!



    All this and more is available at https://github.com/badoo/MockJS



    This project has been released by Badoo Trading Limited under MIT License.

    Thank you to Will for introducing me to Mockito and Diego for helping with releasing it open source.

    And thank you to the original Mockito’s bartenders for creating such an inspiring drink ;)

Notes

  1. code27 reblogged this from 9treeblog
  2. carlos-ouro reblogged this from 9treeblog
  3. 9treeblog posted this