I don't seem to use abstract classes much anymore. The tendency is for me to prefer interfaces over abstract classes as I've been inculcated with mantra 'composition over inheritance'. Where common behaviour is observed though it just feels natural to shove that behaviour in , I dunno, something like a superclass.
I like the template pattern too. So I do use abstract classes occasionally.
Concerning testing I have tended to duplicate testing this behaviour in the past. This is something that has not settled well. Duplication in testing may be slightly less repugnant than duplication in production code but there must be a valid reason.
How should we tackle testing abstract methods then?
Here are a couple of strategies.
Firstly here is my class
An abstract test class
We can put the tests for the behaviour into an abstract test class using a protected but uninitialised instance member. In our concrete sub class test class we inherit from the abstract test class and instantiate the sub class variable When we run the tests the superclass tests run with the subclasses.
If the subclass has more methods that need to be tested then a clumsy casting of the concrete type over the instance variable has to performed each time we test. Alternatively a separate member of the subclass needs to be setup as well the abstract one.
An mock of the abstract class
Alternatively we can use a mocking framework to mock the abstract class and run tests on that.
A fake concrete class
If the feeling of mocking an abstract class doesn't sit well an slight variation would be to create a fake concrete instance and perform the tests on that.
If I was using Mspec today I could has used its behaviours functionality which may have been more simple but I wanted to see from a TDD point of view what options I had.
Do you use any other strategies for testing abstract classes?
All the code if you want it
2 comments:
I tend not to explicitly test abstract classes.
Like you, I tend to use abstract classes less and less, favouring composition rather than inheritance.
However, IMO - testing an abstract class hints at "white box testing" - when we should be treating our classes as "black box" - exercising that the *interface* works as expected. Therefore, we should have no knowledge of the abstract class - that is an internal engineering concern.
Also, if we were to specifically test the abstract class, it could (theoretically) mean that we neglect thorough testing of the subclasses - and introduce bug in certain types of the abstract class.
In short, favouring composition over inheritance = less abstracts. When you do have abstracts, ignore the fact that they exist and still write a complete suite of tests for the concrete classes interface.
Awesome article. I love studying patterns and follow design guidelines such as preferring composition over inheritance, but sometimes abstracts seem like the right tool for the job. In those rare cases you have provided some really good options. Thank you.
Post a Comment