dunit icon indicating copy to clipboard operation
dunit copied to clipboard

Attempt to implement mocking of shared methods/shared objects.

Open chris0210 opened this issue 11 years ago • 6 comments

Here is my attempt to implement mocking for shared methods. The major problem here is that you can only call shared methods on shared objects. The current implementation of getMock() always returns a not shared object - therefore no shared method could be called.

It was not possible to do things like that:

static class T
    shared void foo();

    mixin Mockable!(T);
}

shared auto t = T.getMock(); // doesn't return shared object
t.mockMethod("foo", ...); // shared object can't call unshared method "mockMethod"

The solution I present here enables mocking of shared object by using the Mockable mixin in the following way:

static class T
    // ...

    mixin Mockable!(shared T);
}

By specifying the shared qualifier the getMock() method will return a shared object. All methods of the mock - like mockMethod() or assertMethodCalls -will by qualified as shared and so you can call them.

The only two problems here are:

  • overloading shared methods leads to code duplication. I solved this using mixins.
  • std.traits doesn't seem to offer a way to determine if a type is shared. I solved this using a hack with stringof()

What's your opinion on this solution?

chris0210 avatar Feb 27 '14 14:02 chris0210

It looks interesting, i'll take a closer look this weekend (when i get more time) and in the meantime i'll have a think about the general design. I wonder if we could do it more elegantly?

I noticed you've use token strings q{...} in this patch, i wonder if these should be used throughout for consistency?

nomad-software avatar Feb 27 '14 17:02 nomad-software

Using token strings wherever there are string literals containing source code is a very good idea. My IDE (Eclipse + DDT) as well as GitHub keeps highlighting the source code even within these string literals.

The only problem I have encountered with these literals is the following: When you try to insert a variable into the string, you cannot just close the braces } use the concatenation operator twice ~ myVarible ~ and open the braces again {. Like so:

template MyMixin(string someVariable) {
    immutable char[] MyMixin = q{
        if (someCondition) {
            } ~ someVariable ~ q{
        }
    };
}

This won't compile because the closing bracket } ~ will match to the opening bracket if {. A possible solution is something like that:

template MyMixin(string someVariable) {
    import std.array : replace;

    immutable char[] MyMixin = q{
        if (someCondition) {
            %someVariable%
        }
    }.replace("%someVariable%", someVariable);
}

Using format() is also an option, as long as you have only a few variables. Otherwise the %s references get confusing.

chris0210 avatar Feb 27 '14 17:02 chris0210

I'll have a closer look to the source code and see, if I have any idea to improve the design....

chris0210 avatar Feb 27 '14 18:02 chris0210

I have found a clean solution to determine if a type is shared. Removed the dirty hack .

chris0210 avatar Feb 27 '14 18:02 chris0210

I have found a clean solution to determine if a type is shared. Removed the dirty hack .

Nice!

I wonder if there is a way of getMock() automatically returning a shared type mock instead of specifying it when adding the mixin?

nomad-software avatar Feb 27 '14 18:02 nomad-software

How about a second method getSharedMock()? So the mixin could return both, a shared and a not shared version of the Mock.

chris0210 avatar Feb 27 '14 19:02 chris0210