In this section, we provide some informations on more advanced usages of opmock.
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 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.
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:
On the other hand, mocks can:
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:
A custom matcher gets:
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!
Inside a callback function, you are “on your own”. This means that:
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.