arrow-left

All pages
gitbookPowered by GitBook
1 of 27

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Creating MockBox

mockBox = new testbox.system.MockBox();

// Within a TestBox Spec
getMockBox()

The factory takes in one constructor argument that is not mandatory: generationPath. This path is a relative path of where the factory generates internal mocking stubs that are included later on at runtime. Therefore, the path must be a path that can be used using cfinclude. The default path the mock factory uses is the following, so you do not have to specify one, just make sure the path has WRITE permissions:

/testbox/system/stubs

Hint If you are using Lucee or ACF10+ you can also decide to use the ram:// resource and place all generated stubs in memory.

MockBox

Create mocks and stubs!

MockBox

hashtag
Introduction

TestBox includes a mocking and stubbing library we lovingly call MockBox. You don't have to install it or have a separate libary, it is part of TestBox.

MockBox shines by allowing you to create mock and stub objects.

hashtag
Important Setup

Write capabilities on disk for the default path of /testbox/system/testings/stubs.

You can also choose the directory destination for stub creations yourself when you initialize TestBox. If using ColdFusion 9 or Lucee you can even use ram:// and use the virtual file system.

hashtag
Useful Resources

$getProperty() Method

This method can help you retrieve any public or private internal state variable so you can do assertions. You can also pass in a scope argument so you can not only retrieve properties from the variables scope but from any nested structure inside of any private scope:

any $getProperty(name [scope='variables']

Parameters:

  • name - The name of the property to retrieve

  • scope - The scope where the property lives in. The default is variables scope.

Our Approach and Benefits

The approach that we take with MockBox is a dynamic and minimalistic approach. Why dynamic? Well, because we dynamically transform target objects into mock form at runtime. The API for the mocking factory is very easy to use and provides you a very simplistic approach to mocking.

We even use $()style method calls so you can easily distinguish when using or mocking methods, properties, etc. So what can MockBox do for me?

  • Create mock objects for you and keep their methods intact (Does not wipe methods, so you can do method spys, or mock helper methods)

$args() Method

This method is used to tell MockBox that you want to mock a method with a SPECIFIC number of argument calls. Then you will have to set the return results for it, but this is absolutely necessary if you need to test an object that makes several method calls to the same method with different arguments, and you need to mock different results coming back. Example, let's say you are using a ColdBox configuration bean that holds configuration data. You make several calls to the getKey() method with different arguments:

How in the world can I mock this? Well, using the mock arguments method.

Hint So remember that if you use the $args() call, you need to tell it what kind of results you are expecting by calling the $results() method after it or you might end up with an exception.

What is Mocking?

"A mock object is an object that takes the place of a 'real' object in such a way that makes testing easier and more meaningful, or in some cases, possible at all". by Scott Bain (Emergent Design )

Here are some examples of real life mocks to get you in the mocking mood:

When doing unit testing of ColdFusion CFCs, we will come to a point where a single class can have multiple external dependencies or collaborators; whether they are classes themselves, data, external APIs, etc. Therefore, in order to unit test our class exclusively and easily we need to be able to mock this behavior or have full control of this behavior. Remember that unit testing is the testing of software units in isolation. If not, we would be instantiating and creating entire set of components, frameworks, pulling network plugs and so much more ridiculous but functional things just to test one single piece of functionality and/or behavior. So in summary, mock objects are just test oriented replacements for collaborators and dependencies.

Create mock objects and wipe out their method signatures
  • Create stub objects for objects that don't even exist yet. So you can build to interfaces and later build dependencies.

  • Decorate instantiated objects with mocking capabilities (So you can mock targeted methods and properties; spys)

  • Mock internal object properties, basically do property injections in any internal scope

  • State-Machine Results. Have a method recycle the results as it is called consecutively. So if you have a method returning two results and you call the method 4 times, the results will be recycled: 1,2,1,2

  • Method call counter, so you can keep track of how many times a method has been called

  • Method arguments call logging, so you can keep track of method calls and their arguments as they are called. This is a great way to find out what was the payload when calling a mocked method

  • Ability to mock results depending on the argument signatures sent to a mocked method with capabilities to even provide state-machine results

  • Ability to mock private/package methods

  • Ability to mock exceptions from methods or make a method throw a controlled exception

  • Ability to change the return type of methods or preserve their signature at runtime, extra cool when using stubs that still have no defined signature

  • Ability to call a debugger method ($debug()) on mocked objects to retrieve extra debugging information about its mocking capabilities and its mocked calls

  • expect( model.$getProperty("dataNumber", "variables") ).toBe( 4 );
    expect( model.$getProperty("name", "variables.instance") ).toBe(  "Luis" );
    If the method you are mocking is called using named arguments then you can mock this using:
    configBean.getKey('DebugMode');
    configBean.getKey('OutgoingMail');
    //get a mock config bean
    mockConfig = getMockBox().createEmptyMock("coldbox.system.beans.ConfigBean");
    //mock the method for positional arguments
    mockConfig.$("getKey").$args("debugmode").$results(true);
    mockConfig.$("getKey").$args("OutgoingMail").$results('[email protected]');
    
    //Then you can call and get the expected results
    //get a mock config bean
    mockConfig = getMockBox().createEmptyMock("coldbox.system.beans.ConfigBean");
    //mock the method for named arguments
    mockConfig.$("getKey").$args(name="debugmode").$results(true);
    Unit testing with mock objects IBM developerWorksarrow-up-right
  • Emergent Design by Scott Bainarrow-up-right

  • Mocks Aren't Stubs by Martin Fowlerarrow-up-right

  • Approaches to Mockingarrow-up-right
    Wikipedia Mock Objectsarrow-up-right
    Using mock objects for complex unit tests IBM developerWorksarrow-up-right

    Mock objects can also be created by hand, but MockBox takes this pain away by leveraging dynamic techniques so that you can Mock dynamically and at runtime. Like Scott Bainarrow-up-right describes in his Emergent Designarrow-up-right book:

    "Mocks are definitely congruent with the Gang of Four (GoF) notion of designing to interfaces, because a mock is essentially the interface without any real implementation." - Scott Bain (Emergent Design)

    You will be leveraging MockBox to create objects that represent your dependencies or even data, decide what methods will return (expectations), mock network connections, exceptions and much more. You can then very easily test the exclusive behavior of components as you will now have control of all expectations, and remember that testing is all about expectations. Also, as your object oriented applications get more complex, mocking becomes essential, but you have to be aware that there are limitations. Not only will you do unit-testing but you will need to expand to do integration testing to make sure the all encompassing behavior is still maintained. However, by using a mocking framework like MockBox you will be able to apply a test-driven development methodology to your unit-testing and be able to accelerate your development and testing. The more you mock, the more you will get a feel for it and find it completely essential when doing unit testing. Welcome to a world where mocking is fun and not frowned upon :)

    The Evolutionary Nature of Professional Software Developmentarrow-up-right

    Creating a Stub Object

    In order to create a stub object you will use the : createStub() method.

    public any createStub([boolean callLogging='true'], [extends], [implements])

    Parameters:

    • callLogging - Add method call logging for all mocked methods

    • extends - Make the stub extend from certain CFC

    • implement - Make the stub adhere to an interface(s)

    This method will create an empty stub object that you can use and mock with methods and properties. It can then be used in any code to satisfy dependencies meanwhile you build them. This is great to start working on projects where you are relying on other teams to build functionality but you have agreed on specific data or code interfaces. It is also super fancy as it can allow you to tell the stub to inherit from another CFC and look like it, or even pass in one or more interfaces that it must implement. If they are interfaces, then MockBox will generate all the necessary methods to satisfy those interfaces.

    hashtag
    CreateStub() Inheritance

    The createStub() method has an argument called extends that accepts a class path. This will create and generate a stub that physically extends that class path directly. This is an amazing way to create stubs that you can override with inherited functionality or just make it look like it is EXACTLY the type of object you want.

    hashtag
    CreateStub() Interfaces

    The createStub() method has an argument called implements that accepts a list of interface class paths you want the stub to implement. MockBox will then generate the stub and it will make sure that it implements all the methods the interfaces have defined as per their contract. This is such a fantastic and easy way to create a stub that looks and feels and actually has the methods an interface needs.

    $property() Method

    This method is used in order to mock an internal property on the target object. Let's say that the object has a private property of userDAO that lives in the variables scope and the lifecycle for the object is controlled by its parent, in this case the user service. This means that this dependency is created by the user service and not injected by an external force or dependency injection framework. How do we mock this? Very easily by using the $property() method on the target object.

    any $property(string propertyName, [string propertyScope='variables'], any mock)

    Parameters:

    • propertyName - The name of the property to mock

    • propertyScope - The scope where the property lives in. The default is variables scope.

    • mock - The object or data to inject and mock

    Not only can you mock properties that are objects, but also mock properties that are simple/complex types. Let's say you have a property in your target object that controls debugging and by default the property is false, but you want to test the debugging capabilities of your class. So we have to mock it to true now, but the property exists in variables.instance.debugMode? No problem mate (Like my friend Mark Mandel says)!

    //decorate our user service with mocking capabilities, just to show a different approach
    userService = getMockBox().prepareMock( createObject("component","model.UserService") );
    
    //create a mock dao and mock the getUsers() method
    mockDAO=getMockBox().createEmptyMock("model.UserDAO").$("getUsers",QueryNew(""));
    
    //Inject it as a property of the user service, since no external injections are found. variables scope is the default.
    userService.$property(propertyName="userDAO",mock=mockDAO);
    
    //Test a user service method that uses the DAO
    results = userService.getUsers();
    assertTrue( isQuery(results) );
    //decorate the cache object with mocking capabilties
    cache = getMockBox().createMock(object=createObject("component","MyCache"));
    
    //mock the debug property
    cache.$property(propertyName="debugMode",propertyScope="instance",mock=true);

    $spy()

    Spy like us!

    MockBox now supports a $spy( method ) method that allows you to spy on methods with all the call log goodness but without removing all the methods. Every other method remains intact, and the actual spied method remains active. We decorate it to track its calls and return data via the $callLog() method.

    Example of CUT:

    void function doSomething(foo){
      // some code here then...
      local.foo = variables.collaborator.callMe(local.foo);
      variables.collaborator.whatever(local.foo);
    }

    Example Test:

    function test_it(){
      local.mocked = createMock( "com.foo. collaborator" )
        .$spy( "callMe" )
        .$spy( "whatever" );
      variables.CUT.$property( "collaborator", "variables", local.mocked );
      assertEquals( 1, local.mocked.$count( "callMe" ) );
      assertEquals( 1, local.mocked.$count( "whatever" ) );
    }
    myService = mockbox.createStub(extends="model.security.MyService");
    myFakeProvider = mockbox.createStub(implements="coldbox.system.cache.ICacheProvider");
    myFakeProvider.getName();

    $() Method

    This is the method that you will call upon in order to mock a method's behavior and return results. This method has the capability of mocking a return value or even making the method throw a controlled exception. By default the mocked method results will be returned all the time the method is called. So if the mocked method is called twice, the results will always be returned.

    Parameters:

    • method - The method you want to mock or spy on

    • returns - The results it must return, if not passed it returns void or you will have to do the mockResults() chain

    • preserveReturnType - If false, the mock will make the returntype of the method equal to ANY

    • throwException - If you want the method call to throw an exception

    • throwType - The type of the exception to throw

    • throwDetail - The detail of the exception to throw

    • throwMessage - The message of the exception to throw

    • callLogging - Will add the machinery to also log the incoming arguments to each subsequent calls to this method

    • preserveArguments - If true, argument signatures are kept, else they are ignored. If true, BEWARE with $args() matching as default values and missing arguments need to be passed too.

    • callback - A callback to execute that should return the desired results, this can be a UDF or closure. It also receives all caller arguments as well.

    • throwErrorCode - The error code to throw in the exception

    The cool thing about this method is that it also returns the same instance of the object. Therefore, you can use it to chain calls to the object and do multiple mocking of methods all within the same line. Remember that if no returns argument is provided then the return is void

    hashtag
    Examples

    Let's do some samples now

    Mocking Methods

    Come mock with me!

    Once you have created a mock object, you can use it like the real object, which will respond exactly as it was coded. However, you can override its behavior by using the mocking methods placed on the mocked object at run-time. The methods that you can call upon in your object are the following (we will review them in detail later):

    Method Name

    Return Type

    Description

    $()

    Object

    Used to mock a method on the mock object that can return, throw or be a void method.

    $args()

    Object

    $results() Method

    This method can only be used in conjunction with $() as a chained call as it needs to know for what method are the results for.

    The purpose of this method is to make a method return more than 1 result in a specific repeating sequence. This means that if you set the mock results to be 2 results and you call your method 4 times, the sequence will repeat itself 1 time. MUMBO JUMBO, show me!! Ok Ok, hold your horses.

    As you can see, the sequence repeats itself once the call counter increases. Let's say that you have a test where the first call to a user object's isAuthorized() method is false but then it has to be true. Then you can do this:

    $throws() Method

    This method is used to tell MockBox that you want to mock a method with to throw a specific exception. The exception will be thrown instead of the method returning results. This is an alternative to passing the exception in the initial $() call. In addition to the fluent API, the $throws() method also has the benefit of being able to be tied to specific $args() in a mocked object.

    To continue with our getKey() example:

    We want to test that keys that don't exists throw a MissingSetting exception. Let's do that using the $throws() method:

    $once()

    This method can help you verify that only ONE mocked method call has been made on the entire mock or a specific mocked method. Useful alias!

    Parameters:

    • methodName - The optional method name to assert the number of method calls

    Examples:

    $never()

    This method is a quick notation for the $times(0) call but more expressive when written in code:

    Parameters:

    * methodName - The optional method name to assert the number of method calls

    Examples:

    $querySim() Method

    This method is NOT injected into mock objects but avaialble via MockBox directly in order to create queries very quickly. This is a great way to simulate cfquery calls, cfdirectory or any other cf tag that returns a query.

    any $(string method, [any returns], boolean preserveReturnType='true', [boolean throwException='false'], [string throwType=''], [string throwDetail=''], [string throwMessage=''], [boolean callLogging='false'], [boolean preserveArguments='false'], [any callback])

    Mock 1 or more arguments in sequential or named order. Must be called concatenated to a $() call and must be followed by a concatenated $results() call so the results are matched to specific arguments.

    $getProperty(name, scope)

    any

    Retrieve any public or private internal state variable so you can do assertions and more mocking.

    $property()

    Object

    Mock a property in the object on any scope.

    querySim()

    query

    to denote columns. Ex: id, name 1 Luis Majano 2 Joe Louis

    $results()

    Object

    Mock 1 or more results of a mock method call must be chained to a $() or $().$args() call

    $spy( method )

    Object

    Spy on a specific method to check how often it has been called and with what arguments and results.

    $throws(

    type,

    message,

    datail,

    errorcode

    )

    Object

    This method tells MockBox that you want to mock a method that will throw an exception when called.

    Conclusion

    Now you have seen the power of the MockBox! A minimalistic and simplistic approach to mocking and stubbing for ColdFusion (CFML) applications. Welcome to the world of Mocks!!

    $(...).$results(...)
    //Mock 3 values for the getSetting method
    controller.$("getSetting").$results(true,"cacheEnabled","myapp.model");
    
    //Call getSetting 1
    <cfdump var="#controller.getSetting()#">
    Results = true
    
    //Call getSetting 2
    <cfdump var="#controller.getSetting()#">
    Results = "cacheEnabled"
    
    //Call getSetting 3
    <cfdump var="#controller.getSetting()#">
    Results = "myapp.model"
    
    //Call getSetting 4
    <cfdump var="#controller.getSetting()#">
    Results = true
    
    //Call getSetting 5
    <cfdump var="#controller.getSetting()#">
    Results = "cacheEnabled"
    mockUser = getMockBox().createMock("model.User");
    mockUser.$("isAuthorized").$results(false,true);

    Hint Remember that the $throws() call must be chained to a $() or a $args() call.

    configBean.getKey('DebugMode'); // Exists
    configBean.getKey('OutgoingMail'); // Exists
    configBean.getKey('IncmingMail'); // Does not exist (see the typo?)
    // get a mock config bean
    mockConfig = getMockBox().createEmptyMock( "coldbox.system.beans.ConfigBean" );
    // mock the method with args
    mockConfig.$( "getKey" ).$args( "debugmode" ).$results( true );
    mockConfig.$( "getKey" ).$args( "OutgoingMail" ).$results( "[email protected]" );
    
    // Here's the new $throw call
    mockConfig.$( "getKey" ).$args( "IncmingMail" ).$throws( type = "MissingSetting" );
    
    // Then you can call and get the expected results
    expect( function(){
        mockConfig.getKey( "IncmingMail" );
    } ).toThrow( "MissingSetting" );
    Boolean $once([methodname])
    // let's say we have a service that verifies user credentials
    // and if not valid, then tries to check if the user can be inflated from a cookie
    // and then verified again
    function verifyUser(){
    
        if( isValidUser() ){
            log.info("user is valid, doing valid operations");
        }
    
        // check if user cookie exists
        if( isUserCookieValid() ){
            // inflate credentials
            inflateUserFromCookie();
            // Validate them again
            if( NOT isValidUser() ){
                log.error("user from cookie invalid, aborting");
            }
        }
    }
    
    // Now the test
    it( "can verify a user", function(){
        security = getMockBox().createMock("model.security").$("isValidUser",false);
        security.storeUserCookie("valid");
        security.verifyUser();
    
        expect( security.$once("isValidUser") ).toBeTrue();
    });
    Boolean $never([methodname])
    security = getMockBox().createMock("model.security");
    
    //No calls yet
    expect( security.$never() ).toBeTrue();
    
    security.$("isValidUser",false);
    security.isValidUser();
    
    // Asserts
    expect( security.$never("isValidUser") ).toBeFalse();
    function testGetUsers(){
        // Mock a query
        mockQuery = mockBox.querySim("id,fname,lname
        1 | luis | majano
        2 | joe | louis
        3 | bob | lainez");
    
        // tell the dao to return this query
        mockDAO.$("getUsers", mockQuery);
    }

    $debug()

    This method is used for debugging purposes. If you would like to get a structure of all the mocking internals of an object, just call this method and it will return to you a structure of data that you can dump for debugging purposes.

    <cfdump var="#targetObject.$debug()#">
    function beforeAll(){
        mockUser = createMock("model.security.User");
    
        //Mock several methods all in one shot!
        mockUser.$("isFound",false).$("isDirty",false).$("isSaved",true);
    }
    //make exists return true in a mocked session object
    mockSession.$(method="exists",returns=true);
    expect(mockSession.exists('whatevermanKey')).toBeTrue();
    
    //make exists return true and then false and then repeat the sequence
    mockSession.$(method="exists").$results(true,false);
    expect( mockSession.exists('yeaaaaa') ).toBeTrue();
    expect( mockSession.exists('nada') ).toBeFalse();
    
    //make the getVar return a mock User object
    mockUser = createMock(className="model.User");
    mockSession.$(method="getVar",results=mockUser);
    
    expect( mockSession.getVar('sure') ).toBe( mockUser );
    
    //Make the call to user.checkPermission() throw an invalid exception
    mockUser.$(method="checkPermission",
            throwException=true,
            throwType="InvalidPermissionException",
            throwMessage="Invalid permission detected",
            throwDetail="The permission you sent was invalid, please try again.");
    
    try{
        mockUser.checkPermission('invalid');
    }
    catch(Any e){
        if( e.type neq "InvalidPermissionException"){
          fail('The type was invalid #e.type#');
        }
    }
    
    //mock a method with call logging
    mockSession.$(method="setVar",callLogging=true);
    mockSession.setVar("Hello","Luis");
    mockSession.setVar("Name","luis majano");
    //dump the call logs
    <cfdump var="#mockSession.$callLog()#">

    $count()

    Get the number of times a method has been called or the entire number of calls made to ANY mocked method on this mock object. If the method has never been called, you will receive a 0. If the method does not exist or has not been mocked, then it will return a -1.

    Parameters:

    • methodName - Name of the method to get the counter for (Optional)

    $reset()

    This method is a utility method used to clear out all call logging and method counters.

    numeric $count([string methodName])
    mockUser = getMockBox().createMock("model.User");
    mockUser.$("isAuthorized").$results(false,true);
    
    debug(mockUser.$count("isAuthorized"));
    //DUMPS 0
    
    mockUser.isAuthorized();
    debug(mockUser.$count("isAuthorized"));
    //DUMPS 1
    
    mockUser.isAuthorized();
    debug(mockUser.$count("isAuthorized"));
    //DUMPS 2
    
    // dumps 2 also
    debug( mockUser.$count() );
    void $reset()
    security = getMockBox().createMock("model.security").$("isValidUser", true);
    security.isValidUser( mockUser );
    
    // now clear out all call logs and test again
    security.$reset();
    mockUser.$property("authorized","variables",true);
    security.isValidUser( mockUser );

    $atLeast()

    This method can help you verify that at least a minimum number of calls have been made to all mocked methods or a specific mocked method.

    Boolean $atLeast(minNumberOfInvocations,[methodname])

    Parameters:

    • minNumberOfInvocations - The min number of calls to assert

    • methodName - The optional method name to assert the number of method calls

    // let's say we have a service that verifies user credentials
    // and if not valid, then tries to check if the user can be inflated from a cookie
    // and then verified again
    function verifyUser(){
    
        if( isValidUser() ){
            log.info("user is valid, doing valid operations");
        }
    
        // check if user cookie exists
        if( isUserCookieValid() ){
            // inflate credentials
            inflateUserFromCookie();
            // Validate them again
            if( NOT isValidUser() ){
                log.error("user from cookie invalid, aborting");
            }
        }
    }
    
    // Now the test
    it( "can verify a user", function(){
        security = createMock("model.security").$("isValidUser",false);
        security.storeUserCookie("invalid");
        security.verifyUser();
    
        // Asserts that isValidUser() has been called at least 5 times
        expect( security.$atLeast(5,"isValidUser") ).toBeFalse();
        // Asserts that isValidUser() has been called at least 2 times
        expect( security.$atLeast(2,"isValidUser") ).toBeFalse();
    });

    Verification Methods

    The following methods are also mixed in at run-time into mock objects and they will be used to verify behavior and calls from these mock/stub objects. These are great in order to find out how many mocked methods calls have been made and to find out the arguments that where passed to each mocked method call.

    Method Name

    Return Type

    Description

    $count([methodName])

    Numeric

    Get the number of times all mocked methods have been called on a mock or pass in a method name and get a the method's call count.

    $times(count,[methodName]) or $verifyCallCount(count,[methodName])

    Numeric

    Creating a Mock Object

    In order to create a mock object you need to use any of the following methods: createMock(), createEmptyMock(), or prepareMock().

    hashtag
    createMock()

    Used to create a new mock object from scratch or from an already instantiated object.

    $times() or $verifyCallCount()

    This method is used to assert how many times a mocked method has been called or ANY mocked method has been called.

    Parameters:

    • count - The number of times any method or a specific mocked method has been called

    • methodName - The optional method name to assert the number of method calls

    Assert how many calls have been made to the mock or a specific mock method

    $never([methodName])

    Boolean

    Assert that no interactions have been made to the mock or a specific mock method: Alias to $times(0)

    $atLeast(minNumberOfInvocations,[methodName])

    Boolean

    Assert that at least a certain number of calls have been made on the mock or a specific mock method

    $once([methodName])

    Boolean

    Assert that only 1 call has been made on the mock or a specific mock method

    $atMost(maxNumberOfInvocations, [methodName])

    Boolean

    Assert that at most a certain number of calls have been made on the mock or a specific mock method.

    $callLog()

    struct

    Retrieve the method call logger structure of all mocked method calls.

    $reset()

    void

    Reset all mock counters and logs on the targeted mock.

    $debug()

    struct

    Retrieve a structure of mocking debugging information about a mock object.

    $callLog()

    This method is used to retrieve a structure of method calls that have been made on mocked methods of the mock object. This is extermely useful when you want to assert that a certain method was called with the appropriate arguments. Great for testing method calls that save or update data to some kind of persistent storage. Also great to find out what was the state of the data of a call at certain points in time.

    Each mocked method is a key in the structure that contains an array of calls. Each array element can have 0 or more arguments that are traced when methods where called with arguments. If they where made with ordered or named arguments, you will be able to know the difference. We recommend dumping out the structure to check out its composition.

    struct $callLog()

    Examples:

    security = getMockBox().createMock("model.security");
    //Call methods on it that perform something, but mock the saveUserState method, it returns void
    security.$("saveUserState");
    
    //get the call log for this method
    userStateLog = security.$callLog().saveUserState;
    expect( arrayLen(userStateLog) eq 0 ).toBeTrue();
    Examples
    Boolean $times(numeric count, [methodname])
    security = getMockBox().createMock("model.security");
    
    //No calls yet
    expect( security.$times(0) ).toBeTrue();
    
    security.$("isValidUser",false);
    security.isValidUser();
    
    // Asserts
    expect( security.$times(1) ).toBeTrue();
    expect( security.$times(1,"isValidUser") ).toBeTrue();
    
    security.$("authenticate",true);
    security.authenticate("username","password");
    
    expect( security.$times(2) ).toBeTrue();
    expect( security.$times(1,"authenticate") ).toBeTrue();
    Parameters:
    • className - The class name of the object to create and mock

    • object - The instantiated object to add mocking capabilities to, similar to using prepareMock()

    • clearMethods - If true, all methods in the target mock object will be removed. You can then mock only the methods that you want to mock

    • callLogging - Add method call logging for all mocked methods only

    hashtag
    createEmptyMock()

    Used to create a new mock object with all its method signatures wiped out, basically an interface with no real implementation. It will be up to you to mock all behavior.

    Parameters:

    • className - The class name of the object to create and mock

    • object - The instantiated object to add mocking capabilities to, similar to using prepareMock()

    • callLogging - Add method call logging for all mocked methods only

    hashtag
    prepareMock()

    Decorate an already instantiated object with mocking capabilities. It does not wipe out the object's methods or signature, it only decorates it (mixes-in methods) with methods for mocking operations. This is great for doing targeted mocking for specific methods, private methods, properties and more.

    Parameters:

    • object - The already instantiated object to prepare for mocking

    • callLogging - Add method call logging for all mocked methods only

    Caution If call logging is turned on, then the mock object will keep track of all method calls to mocked methods ONLY. It will store them in a sequential array with all the arguments the method was called with (named or ordered). This is essential if you need to investigate if a method was called and with what arguments. You can also use this to inspect save or update calls based on mocked external repositories.

    Sample:

    Let's say that we have a user service layer object that relies on the following objects:

    • sessionstorage - a session facade object

    • transfer - the transfer ORM

    • userDAO - a data access object for complex query operations

    We can start testing our user service and mocking its dependencies by preparing it in a test case CFC with the following setup() method:

    The service CFC we just injected mocked dependencies:

    public any createMock([string CLASSNAME], [any OBJECT], [boolean CLEARMETHODS='false'], [boolean CALLLOGGING='true'])
    collaborator = mockbox.createMock("model.myClass");
    public any createEmptyMock(string CLASSNAME, [any OBJECT], [boolean CALLLOGGING='true'])
    user = mockbox.createEmptyMock("model.User");
    public any prepareMock([any OBJECT], [boolean CALLLOGGING='true'])
    myService = createObject("component","model.services.MyCoolService").init();
    // prepare it for mocking
    mockBox.prepareMock( myService );
    component extends=”testbox.system.BaseSpec” {
    
        function beforeAll(){
            //Create the User Service to test, do not remove methods, just prepare for mocking.
            userService = createMock("model.UserService");
    
            // Mock the session facade, I am using the coldbox one, it can be any facade though
            mockSession= createEmptyMock(className='coldbox.system.plugins.SessionStorage');
    
            // Mock Transfer
            mockTransfer = createEmptyMock(className='transfer.com.Transfer');
    
            // Mock DAO
            mockDAO = createEmptyMock(className='model.UserDAO');
    
            //Init the User Service    with mock dependencies
            userService.init(mockTransfer,mockSession,mockDAO);
        }
    
        function run(){
    
            describe( "User Service", function(){
                it( "can get data", function(){
                    // mock a query using mockbox's querysimulator
                    mockQuery = querySim("id, name
                    1|Luis Majano
                    2|Alexia Majano");
                    // mock the DAO call with this mocked query as its return
                    mockDAO.$("getData", mockQuery);
    
                    data = userService.getData();
                    expect( data ).toBe( mockQuery );
                });
            });
    
        }
    
    }
    <cfcomponent name="UserService" output="False">
    
    <cffunction name="init" returntype="UserService" output="False">
      <cfargument name="transfer">
      <cfargument name="sessionStorage">
      <cfargument name="userDAO">
      <cfscript>
        instance.transfer = arguments.transfer;
        instance.sessionStorage = arguments.sessionStorage;
        instance.userDAO = arguments.userDAO;
    
        return this;
      </cfscript>
    </cffunction>
    
    <cffunction name="getData" returntype="query" output="false">
        <cfreturn instance.userDao.getData()>
    </cffunction>
    
    </cfcomponent>

    Some Examples

    hashtag
    The Collaborator

    The premise of a collaborator is that it is an object that collaborates with another object for some purpose. Usually this is a dependency of some sort. Now, you can have a collaborator that is injected into the target object by some form of dependency injection, or it is created by the target object. Either way, you need to be able to mock the behavior of this collaborator object. The latter approach is much harder to test because you are doing a createObject() call. However, you need to put into practice your testable and refactor skills to work, in order to make this more testable. Thanks Marc Esher!

    Example:

    The source above is too hard to test because you have no control over the create object call. Therefore you can have two solutions for this, which actually makes your code more testable.

    1. Have a method return to you an instance of the Validator object.

    2. Have the Validator object be injected via dependency injection into your target object.

    Most of the time collaborators can be injected by dependency injection, but sometimes they will not as they are only in use by the target object. So let's do both approaches, starting with dependency injection via annotations: cfproperty.

    Now that we have a annotation injection or a setter injection, let's mock the collaborator into our target object with the mocked method for isValid email.

    There you go. Now let's refactor our method call:

    As you can see, even our method look cleaner and sharper thanks to our refactoring. However, the most important aspect of our refactoring is that now we can mock the collaborator.

    hashtag
    Method Spies

    The premise of method spies is that you want to mock the return of method calls inside the target object that we are testing because these methods are helper methods. Also, maybe these helper methods have an access type of private which we cannot mock directly. Here is a typical example in one of my security objects:

    Ok, this simple liner security validator throws tons of problems. Why? Well, I have three internal private calls:

    • getUserSession() - supposed to return a nice user object

    • isSSOCookieValid() - checks if a single sign on cookie is set

    • authorizeUser() - Authorize a user in my system

    Since I am unit testing this target object's userValidator method, I don't really care about the method calls, I just worry about the behavior that is associated with them because my other tests will go into detail about what they do. However, for my purposes of testing the userValidator method, I just need behavior. Thus, we need to be able to create mocking representations of these methods.

    As you can see from the example above, I mocked all the necessary data and objects to test all the paths that I could follow in my userValidator method. Now we are cooking with MockBox.

    //hard to mock in a method call
    function checkEmail(email){
        var validator = createObject("component","model.util.Validator");
    
        return validator.isEmail(arguments.email);
    }
    <cfproperty name="Validator" type="model" instance="scope" />
    
    //or setter injection
    
    <cffunction name="setValidator" access="public" returntype="void" output="false">
        <cfargument name="validator" type="any">
        <cfset instance.validator = arguments.validator>
    </cffunction>
    targetObject = createObject("component","model.TargetObject");
    //prepare it for mocking, don't remove any methods
    prepareMock(targetObject);
    
    //Create our mock collaborator
    mockValidator = getMockBox().createEmptyMock('model.Validator');
    //Mock the isEmail valid method
    mockValidator.$("isEmail",true);
    
    //Inject it with setter injection
    targetObject.setValidator(mockValidator);
    
    //Inject it via mock property
    targetObject.$property("validator","instance",mockValidator);
    function checkEmail(email){
        return instance.validator.isEmail(arguments.email);
    }
    //or
    function checkEmail(email){
        return getValidator().isEmail(arguments.email);
    }
    function userValidator(rule){
    //validate a user
    var user = getUserSession();
    
    if( user.isAuthorized() ){
        return true;
    }
    
    //Check if single sign on cookie exists and is valid
    if( isSSOCookieValid() ){
        authorizeUser(user);
    }
    else{
        return false;
    }
    }
    function beforeAll(){
        //target object
        security = prepareMock( createObject("component","model.Security") );
        //create a mock user object
        mockUser = createEmptyMock("model.User");
    }
    
    function run(){
    
        describe( "A user", function(){
            it( "can be authenticated", function(){
                //mock user authorizations according to case calls
                mockUser.$("isAuthorized").$results(true,false,false);
                security.$("getUserSession", mockUser);
    
                //case 1: authorized user
                expect(security.userValidator()).toBeTrue();
    
                //case 2: unauthorized user with invalid cookie
                security.$("isSSOCookieValid",false);
                expect( security.userValidator() ).toBeFalse();
    
                //case 3: unauthorized user with valid cookie
                security.$("isSSOCookieValid",true);
                //mock the authorizeUser void call
                security.$("authorizeUser");
                expect( security.userValidator() ).toBeTrue();
            });
        });
    
    }

    $atMost()

    This method can help you verify that at most a maximum number of calls have been made to all mocked methods or a specific mocked method.

    Boolean $atLeast(minNumberOfInvocations,[methodname])

    Parameters:

    • maxNumberOfInvocations - The max number of calls to assert

    • methodName - The optional method name to assert the number of method calls

    Examples:

    // let's say we have a service that verifies user credentials
    // and if not valid, then tries to check if the user can be inflated from a cookie
    // and then verified again
    function verifyUser(){
    
        if( isValidUser() ){
            log.info("user is valid, doing valid operations");
        }
    
        // check if user cookie exists
        if( isUserCookieValid() ){
            // inflate credentials
            inflateUserFromCookie();
            // Validate them again
            if( NOT isValidUser() ){
                log.error("user from cookie invalid, aborting");
            }
        }
    }
    
    // Now the test
    it( "can verify a user", function(){
        security = createMock("model.security").$("isValidUser",false);
        security.storeUserCookie("valid");
        security.verifyUser();
    
        // Asserts that isValidUser() has been called at most 1 times
        expect( security.$atMost(1,"isValidUser") ).toBeFalse();
    });