Wednesday, March 6, 2013

Mocking syntax and MATLAB's method handling shenanigans

One of the first decisions made when starting my thesis was to emulate the python-mockito syntax and provide similar features (namely, stubbing and verification). Why Python and not the original and more popular Java version, I'm not sure. Perhaps simply because when I approached my mentor, I was inquiring about any possible Python projects. In any case, the stubbing syntax boils down to:

when(mock).aFunction('arg').thenReturn('res')

Which actually looks quite clear and intuitive. Now, it was obvious from the start that to support such arbitrary calls to objects, I would have to overload the subsref method in MATLAB, and my initial prototype code did just that. I also knew that obj.method and method(obj) produce the same result in MATLAB, so my code handled only the first variant (it was a feasibility prototype, after all). This turned out to be a mistake - as far as I can see (and others agreed) - calling method(obj) does not, in fact, pass through subsref. As such, the above snippet is just not possible in MATLAB, erroring with: "Undefined variable "when" or class "when"."

But the error did give me an idea? What if I created a class called "when"? It could catch such calls and pass them on to the mock object, whose overloaded subsref could then handle them appropriately. In fact, it could even simplify it and that can only be a good thing. Sure, there would be the extra overhead of creating another class (no, a function wouldn't work), but these are supposed to be used in tests, which are hardly performance-critical (or rather, the added overhead can safely be ignored). Several lines of code later, everything worked great, the above line was correctly caught and I was just writing a script to make my results reproducible, when MATLAB backhanded me with another error:

Static method or constructor invocations cannot be indexed.
Do not follow the call to the static method or constructor with
any additional indexing or dot references.


The error message is clear enough - no indexing after constructors - but the code worked fine from the command prompt. Of course, there's already a StackOverflow question on this: it works in the command prompt and functions, but not in scripts. It even works in cell-mode, just not regular scripts! The answerer on SO seems to think this shouldn't work at all, but I disagree, and another blog gives a decent reason: using Java code in MATLAB. Someone suggested using "eval" to evaluate the code, but that is just not an option in this scenario, as it vastly decreases readability. As such, I've reached an impasse: directly working with the mock object doesn't seem to be a good idea, but creating a "fake" class for some reason doesn't work in scripts.

One line of reasoning is that mocks probably shouldn't be used in scripts (and cell-mode works, anyway) but mostly functions, so using the "when" class isn't a big problem. The issue, of course, is that sooner or later you're going to hit that very cryptic error message. A cleaner solution would be to offer a more verbose syntax for the mock object, perhaps something along the lines of:

mock.when('aFunction').with('arg').thenReturn('res')

Verbose, doesn't flow so well ("doesn't read like a sentance"), not to mention that it's a departure from other frameworks and mockito itself; also not an ideal solution. The third variant is to offer both: mock objects would expose the above API, and the "when" class would just call the mock with the appropriate syntax. But two ways to get the same thing is not good design. At this point I turn to you, dear readers, as I know I've managed to accrue at least a few interested followers. What would you like to see?

1 comment:

  1. Today we also agreed that another alternative syntax shall be possible:

    mock.when.aFunction('arg').thenReturn('res')

    ReplyDelete