On 2014-12-07 15:20, Dicebot wrote:

To give some examples from personal experience of what frustrates me in
typical OOP testing approach:

Imagine we have a simple cache class that internally communicates with
external dht:

class Cache
{
     private DhtClient client;
     this(string addr) { this.client = new DhtClient(addr); }
}

Now we want to test it which implies adding mock client. Canonical way
do it is by dependency injection and interface:

class Cache
{
     private IDhtClient client;
     this(IDhtClient client) { this.client = client; }
}

And know what? This just plain sucks. Not only you are now forced to
modify yoru application code in many places to just comply to
test-related changes that don't help actual app. You also introduce
unnecessary interface indirection potentially killing inlining
opportunities in the process.

Same stuff with policy approach:

class CacheT(DhtClient)
{
     static assert (isDhtClient!DhtClient);

     private DhtClient client;
     this(string addr) { this.client = new DhtClient(addr); }
}

alias Cache = CacheT!DhtClient;

Effectively the same code as sample 1 and makes no difference for rest
of the application. And how would I test it?

alias TestCache = CacheT!StubDhtClient;

No changes to application, no extra indirections, same effective testing
coverage. Much better.

And, of course, any utility functions that contain complex logic are
better moved to free functions so that those can be tested with no
relation to other code at all.

One of the few advantage with dynamic typing and duck typing. Just make sure the object has the correct API then it can be of whatever type you like.

With Ruby it's also very easy to add methods on the fly, change existing ones and bypass private. Features that can be very handy when writing tests.

--
/Jacob Carlborg

Reply via email to