SysMock for Dynamics AX 2009

2

Posted by Alexandr Malapheev | Labels: dynamics ax 2009, mock | Posted on Tuesday, September 8, 2009

What is the mock object?

In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. Mock objects help you design and test the relations between the objects entangling the whole system.

Reasons to use

Developing with Mock objects helps on building a loosely coupled, and therefore maintainable, reusable, and testable code.
In a unit test mock objects can simulate the behavior of complex real (non-mock) objects and are therefore helpful when a real object is impractical or impossible to incorporate into a unit test. If the object has any of the following characteristics, it may be useful to use a mock object in its place:

  1. Supplies non-deterministic results;
  2. Has states that are difficult to create or reproduce;
  3. Is slow (e.g. a complete database, which would have to be initialized before the test);
  4. Does not yet exist or may change behavior;
  5. Would have to include information and methods exclusively for testing purposes (and not for its actual task).

Technical details

Mock objects have the same interface as the real objects they mimic, allowing a client object to remain unaware of whether it is using a real object or a mock object. AxMock allows the programmer to specify which, and in what order, methods will be invoked on a mock object and what parameters will be passed to them, as well as what values will be returned. Thus, the behavior of a complex object can be mimicked by a mock object, allowing the programmer to discover whether the object being tested responds appropriately to the wide variety of states such objects may be in.

The common coding style for testing with mock objects is to:

  1. Create instances of mock objects
  2. Set state and expectations in the mock objects
  3. Invoke domain code with mock objects as parameters
  4. Verify consistency in the mock objects

Example:

Consider we have this useless class we need to test:
class ClassToTest
{
     ClassToMock class2Mock;

     void new(ClassToMock _class2Mock)
     {
          class2Mock = _class2Mock;
     }

     public void doAction()
     {
          class2Mock.method1();
          if(class2Mock.method2() != "hello mock!")
          {
               throw error("error message");
          }     
     }
}


Let’s create unit test for this class using AxMock framework:
public void testClassToTest()
{
     SysMockRepository mocks = new SysMockRepository();
     ClassToMock mock = mocks.strictMock(classStr(ClassToMock));
     ClassToTest class2test = new ClassToTest(mock);

     // setting expectations
     mock.method1();
     SysMockExpect::call(mock.method2()).return("hello mock!");

     mocks.replayAll();

     class2test.doAction();

     mocks.verifyAll();
}

In this test we are creating strict mock object (line 4), creating the class to test (line 5), setting expectations (lines 8, 9), move the mock object to the replay state (line 11), run method we want to test (line 13), verify that all expectations are met (line 15).

Types of mock objects

AxMock framework supports 3 different types of mock objects:
- Strict. Strict semantics means that any call that wasn’t explicitly recorded is considered as error and would cause an exception to be thrown.
- Dynamics. Dynamic semantics means that any call that wasn’t explicitly recorded is accepted and a null or zero is returned (if there is a return value).
- Partial. Means that any call that wasn’t explicitly reordered calls original class method.

Setting expectations:

To set expectation you just need to call a method of a mock object. Or, if you want to set action to the calling method you have to use the Expect class. This class has only one method – “call”, this method takes one parameter of type anytype, and returns interface IMethodOptions.
Using this class you can set a value which will be returned from the method in replay state:
SysMockExpect::call(mock.method2()).return("hello mock!");
Or you can specify the text of error to be thrown:
SysMockExpect::call(mock.method2()).error("error message");
Or you can ask mock object to call original method:
SysMockExpect::call(mock.method2()).originalMethod();