-
Notifications
You must be signed in to change notification settings - Fork 31
Developer Guide
The developer guide provides a comprehensive overview of the features of Mockatoo.
- Create a Mock
- Verifying Behaviour
- Basic Stubbing
- Argument Matchers
- Verifying exact number of invocations / at least once / never
- Advanced Stubbing with consecutive calls or callbacks
- Resetting a mock
- Spying on real objects (since 1.1.0)
- Partial Mocking (since 1.2.0)
- Mocking Properties that are Read or Write only (since 1.2.0)
- Known limitations
Import Mockatoo module
import mockatoo.Mockatoo.*;
Use 'using' mixin to greatly simplify mocking api (since 1.3.0)
using mockatoo.Mockatoo.*;
Mocks can be generated from any Class, Interface or Typedef alias (not typedef structure)
var mockedClass = mock(SomeClass);
var mockedInterface = mock(SomeInterface);
// or without using static imports
Mockatoo.mock(SomeClass);
A Mock class type will be generated that extends the Class (or Interface), stubbing all methods, and generate the code to instantiate the instance:
var mockedClass = new SomeClassMocked();
var mockedInterface = new SomeInterfaceMocked();
If a class requires Type parameters then you can either use a typedef alias.
typedef FooBar = Foo<Bar>;
...
var mockFoo = mock(FooBar);
Or pass through the types as a second parameter
var mockFoo = mock(Foo,[Bar]);
Both these generates the equivalent expressions:
var mockFoo = new FooMocked<Bar>();
Note: These usages are required in order to circumvent limitation of compiler with generics. You cannot compile
Foo.doSomething(Array<String>)
You can also 'mock' a typedef structure, however the generated object is not technically a mock, so does not support verification or stubbing.
typedef SomeTypeDef = {name:String}
...
var mockedTypDef = mock(SomeTypeDef);
Verification refers to validation of of if, and how often a method has been called (invoked) with particular argument values.
To verify that a method foo has been invoked:
mock.someMethod("someValue").verify(); //verify called once
mock.someMethod("someValue").verify(2); //verify called twice
// or without using 'using'
Mockatoo.verify(mock.someMethod("someValue"), 2);
Once created, mock will remember all interactions. Then you can selectively verify whatever interaction you are interested in.
Mockatoo allows the behaviour of methods to be stubbed
mock.someMethod("foo").returns("hello");
// or without using 'using'
Mockatoo.when(mock.someMethod("foo")).thenReturn("hello");
You can also specify an exception
mock.someMethod("bar").throws(new SomeException("is not foo"));
//or without using 'using'
Mockatoo.when(mock.someMethod("bar")).thenThrow(new SomeException("is not foo"));
Mockatoo verifies argument values in natural syntax: by using an equals()
method. Sometimes, when extra flexibility is required then you might use argument matchers. Matchers can be used for both verifying and stubbing fields.
Matching against a type:
mock.foo(anyString).verify();
//other examples
mock.foo(anyInt).verify();
mock.foo(anyFloat).verify();
mock.foo(anyBool).verify();
mock.foo(anyObject).verify(); //anonymous data structures only (not class instances)
mock.foo(anyIterator).verify(); // any Iterator or Iterable (e.g. Array, Hash, etc)
mock.foo(anyEnum).verify(); // any enum value of any enum;
Matching against a specific class or enum:
mock.foo(enumOf(Color)).verify(); // any enum value of Enum Colour
mock.foo(instanceOf(SomeClass)).verify(); // any instance of SomeClass (or it's subclasses)
Wildcard matches
mock.foo(any).verify(); //any value (including null)
mock.foo(isNotNull).verify(); // any non null value
Custom matching function
var f = function(value:Dynamic):Bool
{
return value == "foo";
}
mock.foo(customMatcher(f)).verify();
Verifications use natural language to specify the minimum and maximum times a method was invoked with specific arguments
mock.foo().verify(2);
mock.foo().verify(times(2));
mock.foo().verify(atLeast(2));
mock.foo().verify(atLeastOnce);
mock.foo().verify(never);
Note: Default mode is times(1);
Stubbing is chain-able, so you can stub different behaviors for consecutive method calls.
This can be achieved by providing multiple return (or thrown) values:
mock.someMethod().returns("a", "b");
mock.someMethod().throws("one", "two");
Combinations can also be chained together
Mockatoo.when(mock.someMethod()).thenReturn("one", "two").thenThrow("empty");
The last stubbing (e.g: thenThrow("empty")) determines the behavior for any further consecutive calls.
Custom Callback Stub
You can also set a custom callback when a method is invoked
var f = function(args:Array<Dynamic>)
{
if(Std.is(args[0], String) return args[0].charAt(0) == "b";
}
mock.foo("bar")).calls(f);
//or without using 'using'
Mockatoo.when(mock.foo("bar")).thenCall(f);
Mockatoo supports several other stubbing options defined later in this document
mock.foo("bar")).callsRealMethod(); // calls out to real method
mock.foo("bar")).stub(); // resets default stubbing behaviour
You can reset a mock to remove any custom stubs and/or verifications
Mockatoo.reset(mock);
You can create spies of real objects. When you use the spy then the real methods are called unless a method is explicitly stubbed.
Real spies should be used carefully and occasionally, for example when dealing with legacy code.
var mock = spy(SomeClass);
//or without using static imports
var mock = Mockatoo.spy(SomeClass);
All method calls (both real and stubbed) are recorded for verification
mock.someMethod();//calls real method
mock.someMethod().returns("one");
mock.someMethod();//returns stub value "one";
mock.someMethod().verify(2); //both calls recorded
You can also quickly convert a real method to the default stub behaviour:
mock.someMethod().stub();
mock.someMethod(); //returns default mock value (usually null)
There are several limitations to spying:
-
The real constructor cannot be accessed directly, and will always be called with the default values for each argument type - e.g:
super(null,null,null);
-
Interfaces cannot be spied (Mockatoo will ignore these and used normal mocking instead)
You can explicitly set a mock to call out to the underlying real method
mock.someMethod().callsRealMethod();
Like spies, you can also reset them to their mocked behaviour
mock.someMethod().stub();
A property that is read/write only, or calls out to getter/setter functions can be stubbed to a specific return value:
mock.someReadOnlyProperty.returns("foo");
If a property has a getter function, stubbing a return value will automatically stub the underlying getter method - the equivalent of:
Mockatoo.when(mock.get_someReadOnlyProperty()).thenReturn("foo");
Getters can also be stubbed to a custom callback:
mock.someGetter.calls(f);
Both Getters and Setters can also be stubbed with an exception:
mock.someGetter).throws("foo");
mock.someSetter).throws("foo");
var result = mock.someGetter;//throws exception
mock.someSetter = "a";//throws exception
Note: If a property has both a getter and setter, both getting and setting the property with trigger the exception
There are some limitations to property stubbing:
- stubbed properties cannot be chained (thenReturn("foo", "bar", "etc"))
- only getters support stubbed callbacks (
thenCall(f)
) - only getter and setters support stubbed exceptions (
thenThrow(xxx)
)
Inlined methods can only be mocked by using the --no-inline
compiler flag (Haxe 3.x). Otherwise Mockatoo will print a compiler warning and skip affected fields. To ignore compiler warnings, use the -D ignore-inline
compiler flag (added Mockatoo 3.3.x)
inline public function someMethod()
{
...///
}
Mockatoo supports overriding @:final classes and methods in targets other than flash.
@:final public function someMethod()
{
..///
}
Mocking a @:final class in flash will throw a compiler error.
Mocking a @:final method in flash will generate a compiler warning and leave the real method untouched.
See http://code.google.com/p/haxe/issues/detail?id=1246 for more details.