08 March 2008

Another round in the testability debate

This time a posting from Mark Seemann has raised a slew of comments.

One of them is a note from Colin Jack about the annoyance of producing interface/implementation pairs all the time. My first response is that that sounds to me a bit like a problem with style. Maybe it's just wordplay, but usually I don't extract interfaces from classes, I implement interfaces that I've already discovered in some previous test.

My second response is to wonder how much this is a tools issue. I don't believe there's anything in the .Net world that yet matches the responsiveness of the usual Java IDE's. It makes a difference as to what's plausible. I remember the huge shift in perception when first VisualAge for Java and then JetBrains' Idea came out. In retrospect, I always spent more on time on rework than many people I worked with 1 but it sure took a lot more time in emacs (and I was pretty good at it), even if I was working in a better language.


1) which is not to say who was right, I'm just wired that way...

5 comments:

Colin Jack said...

To be fair you are quite right, having said that your approach is quite different than what most people seem to do.

I have been toying with the approach you guys use again. I can certainly see the advantages but in some cases I am producing interface-implementation pairs even when specifying the collaborations up front.

For example for a service that coordinates the creation of an XML file I specified that it would collaborate with a IXmlFileNameProvider. In reality though thats a pathetically small class and showing that the SUT has a dependency on it isn't as important as showing that it has a dependency on (for example) the file system (which I am wrapping).

It's not necessarily a good extension point either and if I did need to swap to a different name provider later I could have quickly extracted a suitable interface and injected at the time.

So really I'm thinking that the upfront collaboration tests can be useful but to me they are just one tool. I'm also still not convinced that mocking based testing/design is inherently better, to be honest I sometimes think concrete collaborations are fine (for now at least). I guess the key thing for me is that just because a class doesn't support one or more interfaces doesn't necessarily mean that it or related design is "legacy".

I'm also not sure what you mean on the tooling front.

Steve Freeman said...

Nobody, including us, says you have to mock everything -- we even have a Test Smell for doing that. That said, there are quite a few people who have dismissed our approach without understanding it.


It depends what your IXmlFileNameProvider is. If it's just a little policy object, then maybe you should enclose it in its caller. If it's protecting you from the file system (in the sense that it allows you unit test locally and split out the integration tests), then maybe it's serving a purpose. An uninformed guess would be that you could wrap up everything to do with streaming out XML into a larger type, which can be integration tested, rather than being so fine-grained.

My last experience of VisualStudio, even with Resharper, is that the environment gets in the way much more than Idea or Eclipse. It's just that little bit harder to make changes. And the 'I' wart on front of interfaces doesn't help...

Colin Jack said...

> Nobody, including us, says you
> have to mock everything -- we
> even have a Test Smell for doing
> that. That said, there are quite
> a few people who have dismissed
> our approach without
> understanding it.

For sure, I've read that that and I wasn't intentionally saying that you do push for mocking everything. Plus I'd never mock a value object or a 3rd party API (I'd wrap it instead in most cases).

> It depends what your
> IXmlFileNameProvider is. If it's
> just a little policy object,
> then maybe you should enclose it
> in its caller. If it's
> protecting you from the file
> system
> ...
> An uninformed guess would be
> that you could wrap up
> everything to do with streaming
> out XML into a larger type,
> which can be integration tested,
> rather than being so fine-
> grained.

I agree completely, I already had the higher level type so I was thinking of doing what you suggest. The thing is though, I'm not 100% sure that this sort of decision making is clear from the documents you guys have so far put out (though I may not have read it all yet).

Anyway I'm encapsulating the file system seperately using an IFileSystem so it is just a Policy. My first version had this style of constructor for the higher level service (SUT):

// Forget the names, its a work in progress :)
public XmlFileBasedSender(IXmlFileNameProvider , IXmlFileConfigurationService ,
IFileSystem, INotificationToXmlConverter)

A couple of those are meaningful interfaces but two of them (IXmlFileNameProvider/INotificationToXmlConverter) are really trivial. Even for these later two cases I want seperate classes but as you say a little concrete coupling here seems to me to be OK.

In fact even if I did enclose them in the caller I could still write a collaboration test that showed the interactions with those concrete (and not injected) components using TypeMock (I think it might be more confusing than helpful though).

I'm also still not sold on using this approach for the domain model, I'm quite happy with *some* concrete coupling and the use of real domain entities in tests (object mother or builders). I guess thats what makes me (in Fowler speak) a classicist though.

So at the minute I'm trying to find out how to take what you guys are pushing for and fit into my own working, I have to say I'm looking forward to your book which I'm sure will clear things up a bit.


> My last experience of
> VisualStudio, even with
> Resharper, is that the
> environment gets in the way much
> more than Idea or Eclipse.

Could be, I've never done much Java so I'm not massively informed on the different IDE's. Personally its more about what I think is right rather than ease, but it wouldn't surprise me if Java IDE's were way ahead.

Chris Nash said...

I believe tools are a big part of it, but not the whole story. As an 'SOA guy', I seem to have got into a lot of arguments recently with the (typically younger) 'DDD guys', simply because of their apparent contradiction that:

* since it's the only implementation of a domain object, there's no need for an interface;
* but since it's a rich domain object that possesses behavior, surely it needs to be mocked.

As far as Java goes, using something like EasyMock on an interface is far simpler than using the class extension to mock a concrete class, and you cannot always effectively do so (for instance, final methods can't be mocked, and the class needs a parameterless constructor). Coding for testability is one thing, but coding around the limitations of a test tool is surely a smell.

I've been working on a series of articles to introduce these concepts to new developers and I've already seen this issue is going to be a hard sell. I'll end up going for the interface-implementation pairs from a testability standpoint, then find that hard to defend if my reasoning behind it is either tool limitations or, worse still, a necessity to change the object design so the tool would function appropriately.

Steve Freeman said...

@Chris. Thanks for your comment. You might want to read around some more of our material -- it's a bit thin at the moment but there's more coming.

First, the point about an interface is not how many times it's implemented, but whether it describes a meaningful role in the domain of the system. This seems to get lost the way OO design is often taught.

Second, the point is not about shoehorning the design to fit the tool. It's more that we find often enough that listening to difficulties in testing leads us to (what we think are) better designs. It's about turning a constraint into an benefit.