Menu

Advanced usages

pogn

In this section, we provide some informations on more advanced usages of opmock.

Writing custom matchers

Opmock comes with some pre defined matchers for common types like int or a c string. However, you'll probably want at some point to write your own matchers, for example to compare 2 complex structures or even to compare 2 classes.
A matcher must respect the following prototype:

typedef int (* OPMOCK_MATCHER)( void *  a, 
                void * b, 
                const char * name, 
                char * message);

This prototype is defined in the header opmock.h.
Your matcher will get:

  • a void * on the first parameter to compare (which is the expected value)
  • a void * on the second parameter to compare (which is the actual value, that is, the value the matcher will receive at run time in the scope of a test). Note that you'll likely have to cast your parameter to a void *.
  • the name of the parameter to compare
  • a pointer on a buffer that will be filled with an error description if the 2 input values are different

A matcher must return 0 if the two parameters are equal, and 1 otherwise.

Below is an example of a custom matcher. You can find it in the samples/c_regression folder in the opmock distribution. This matcher compares 2 structs.

static int compare_toto_value(  void *val1, void *val2, 
                        const char * name, char *buffer) 
{ 
  Toto *toto1 = (Toto *) val1; 
  Toto *toto2 = (Toto *) val2; 
  if(toto1->foo != toto2->foo) { 
    snprintf(buffer, OP_MATCHER_MESSAGE_LENGTH, 
        "parameter '%s.foo' has value '%d', was expecting '%d'", 
        name, toto2->foo, toto1->foo); 
    return 1; 
  } 
  if(toto1->boo != toto2->boo) { 
    snprintf(buffer, OP_MATCHER_MESSAGE_LENGTH, 
        "parameter '%s.boo' has value '%f', was expecting '%f'", 
        name, toto2->boo, toto1->boo); 
    return 1; 
  } 
  return 0; 
}

WARNING : if the parameters to compare in a matcher are pointers (like char *, or a struct pointer), it's a pointer on the pointer that will be stored by opmock. This means that in your matcher, you need to de reference 2 times the value. An example from the matcher cmp_cstring:

int cmp_cstr(void *a, void *b, const char * name, char *message) 
{ 
    char * my_a = *((char **)a); 
    char * my_b = *((char **)b);

...

NOTE : when you return 1 from a custom matcher, opmock will store your error message in the test error stack automatically. If in your test you call OP_VERIFY() (or if you use an equivalent code with another unit testing framework), the test will fail because of this error.

Mixing mocks and matchers to avoid the use of callbacks

Let's consider this function you want to mock:

typedef struct 
{ 
  int foo; 
  float boo; 
  int multiply; 
} FooStruct; 

void callInOut(FooStruct *foo);

This function must perform a side effect on the foo parameter : this is both an in and an out parameter.
You can do this easily with a callback. After all, in a callback, you control exactly what you want to do. But callbacks have some disadvantages when compared to mocks:

  • callbacks don't maintain a call stack. This means that you can't check the order of the * calls for example. Callback calls are never checked in a VERIFY macro.
  • using a callback resets the call stack of a function. The consequence is that you can't mix mocks and callbacks for the same function in the scope of a test (unless you know exactly what you're doing – but this is error prone and fragile).
  • You can't record expected value of parameters and compare them with the actual values you get in the test. Of course, you can hard code this in the callback logic, but this is cumbersome.

On the other hand, mocks can:

  • record and check parameters
  • check the number of mock calls and the order of the calls
  • optionally use matchers

But they can't perform side effects on parameters, because they don't have the logic for it : how a mock could know how to fill in a struct for example?

There is however a way to couple the advantages of mocks and the advantages of callbacks:

  • Create a custom matcher for parameters for which to want a side effect
  • Use these custom matchers together with your mocks

A custom matcher gets:

  • the expected value of a parameter
  • the actual value of a parameter

It means that a custom matcher can both check your parameters and fill them in!

There's a working example in the samples/c_matchers of the Opmock distribution. Look at the function custom_matcher for all the details!

Failing a test from within a callback function

Inside a callback function, you are “on your own”. This means that:

  • You have to check the input parameters yourself (possibly reusing matchers)
  • You handle yourself your “call stack” using the number of calls parameters
    Mocks can make a test fail when a condition is not met (bad parameters, bad number of calls). You can do the same inside a callback, using a simple function:
opmock_add_error_message(char * error_message);

When you call this function with an error message, opmock will store the error in the test error stack. At the end of the test, if you call the OP_VERIFY() macro, it will fail the test. If you don't use the opmock micro testing framework, you can still retrieve the number of errors in the test using opmock_get_number_of_errors() then check the array of error messages.

NOTE : you don't need to call this function from custom matchers. The return value of a matcher is enough for opmock to record automatically an error for the test.


Related

Wiki: Home