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...
Loading...
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)
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
MockBox can be downloaded from http://www.ortussolutions.com/products/testbox or can be installed via CommandBox as part of the TestBox package.
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:
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 has been designed to work under the following CFML Engines:
Railo 3.1+ (Deprecated)
Lucee 4.5+
Adobe ColdFusion 9+
Write capabilities on disk for the default path of /{mockbox}/system/testings/stubs
. You can also choose the directory destination for stub creations yourself when you initialize MockBox. If using ColdFusion 9 or Railo you can even use ram://
and use the virtual file system.
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.
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.
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.
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.
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)!
In order to create a mock object you need to use any of the following methods: createMock()
, createEmptyMock()
, or prepareMock()
.
Used to create a new mock object from scratch or from an already instantiated object.
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
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
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:
"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 The Evolutionary Nature of Professional Software Development)
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.
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 Bain describes in his Emergent Design 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 :)
Once you have created a mock object, you can use it like the real object as it will respond exactly as it was coded. However, you can override its behavior by using the mocking methods that have been 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):
MockBox is a companion package to TestBox that will give you advanced mocking/stubbing capabilities; hence a Mocking Framework. You can use it from within TestBox or as a standalone package in any other testing framework or CFML application as a data provider.
Download from our downloads page or clone via github. Also read our installation section.
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.
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.
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
Let's do some samples now
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
Examples
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:
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:
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)
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:
Parameters:
name - The name of the property to retrieve
scope - The scope where the property lives in. The default is variables scope.
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:
Hint Remember that the
$throws()
call must be chained to a$()
or a$args()
call.
Method Name
Return Type
Description
$()
The Mock
Used to mock a method on the mock object that can return, throw or be a void method.
$property()
The Mock
Mock a property in the object on any scope.
$getProperty(name, scope)
any
Retrieve any public or private internal state variable so you can do assertions and more mocking.
$results()
The Mock
Mock 1 or more results of a mock method call, must be chained to a $() or $().$args() call
$args()
The Mock
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.
querySim()
query
to denote columns. Ex: id, name 1 Luis Majano 2 Joe Louis
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:
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.
Parameters:
minNumberOfInvocations - The min number of calls to assert
methodName - The optional method name to assert the number of method calls
This method is a utility method used to clear out all call logging and method counters.
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.
Examples:
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.
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.
Parameters:
maxNumberOfInvocations - The max number of calls to assert
methodName - The optional method name to assert the number of method calls
Examples:
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.
Have a method return to you an instance of the Validator object.
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.
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.
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!!
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
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.