A new approach to unit tests
October 20, 2008 – 9:54 pm by Stefano MAESTRI| I’ve written a little update of this post. If you are interested joining discussion started around this blog entry please take a look there |
What does “a new approach to unit tests” mean? Isn’t JUnit or TestNG enough and fine? JUnit (from here on I’ll nominate it only for briefness, but TestNG is the same for my discussion) puts test classes on focus and starts from them all tests. This means in fact that classes under test are considered only in test classes code, the only way a programmer can keep an eye on classes under test is using some kind of naming convention.
With older versions of JUnit you were forced to design your test classes extending a framework’s class and calling methods starting with “test”. So the convention has been to name test classes and test methods with words which could “connect” them with class and method under test. I think you would agree with me TestNG and Junit 4 give us a lot of freedom removing these requirements. Anyway the problem of logical connecting class and methods under test to our tests still remain and most test classes still respect that old convention.
But there are a lot of better ways to name classes and methods! Let me introduce you to Behaviour Driven Development (BDD). Please note BDD is not going to be the main focus of this article, anyway it makes a perfect wedding with my idea. So, let’s go into BDD in as fewer words as possible.
BDD is not only a new way to write tests but also a new form of design by contract
Let me start my introduction quoting the behaviour-driven.org:
BehaviourDrivenDevelopment grew out of a thought experiment based on NeuroLinguisticProgramming techniques. The idea is that the words you use influence the way you think about something
The whole idea is to ask programmers to concentrate on words used to describe a test class or method, because selected words will influence their point of view on the problem. In practice test we will write with a BDD approach will be much more concentrate on the behaviour of class/method under test then on the method itself. Of course this will change the way we test our code a lot, i.e. we will test a method multiple time to verify each behaviour is valid for it.
OK, what if I don’t believe on Neuro Linguistic Programming? Well, from a pure developer point of view, we are defining with our behaviour tests contracts of the class and methods. And moreover the tests results will be absolutely clear (i.e. “shouldAcceptNullValue fails” is a very clear statement also without complex reporting). Let me just provide a simple example to get you an idea:
@Test( expected = IllegalArgumentException.class ) public void shouldNotPermitMethodNull() throws Exception { [..] } @Test( expected = IllegalArgumentException.class ) public void shouldNotPermitEndPointNull() throws Exception { } @Test public void shouldInitWebParams() throws Exception { } @Test public void getHoldersResultShouldReturnHolderForRightParameters() throws Exception { } @Test public void getHoldersResultShouldIgnoreUnknowntParameters() throws Exception { } @Test public void getHoldersResultShouldIgnoreINParameters() throws Exception { } @Test public void shouldRuninvokeForOneWayMethod() throws Exception { } @Test public void shouldRuninvokeForMethods() throws Exception { } @Test public void shouldRuninvokeForMethodsApplyingMapping() throws Exception { }
Do you need a brief introduction about how BDD can be successfully applied to Java (original idea comes from Ruby’s RSpec)? Have a look at this excellent post.
So is BDD enough?
IMHO the answer is no. BDD is great and you should try it, but if you try to put it really in practice you will soon completely loose relations between classes under test and test classes. In BDD not only method names loose testXXX convention, but also test class names may loose their conventional name. Moreover you can have more than one test method insisting on the same method and/or class. For example the previous example isn’t perfect, maybe a specific test class to test getHolder method behaviour would be finer.
Do you need help? Here is my new project TestedBy
What about an annotation to mark classes and methods under test with a reference to test classes and methods? Not bad, isn’t it?
My thoughts started more or less from here, but there is much more than an annotation. I’ve loved so much this idea that I decided to start a new open source project providing testing tools that put class under test at the centre.
Continue this read and I’ll demonstrate you how this annotation and related tools may totally change your approach to tests.
In a nutshell TestedBy aims at changing the point of view regarding test classes and classes under test. What we would obtain is to put classes under test (the most important classes of projects) at the centre and link test classes and methods from them. A code snippet may help much more than any explanations:
public class TestedBySample { /** * @param args */ public static void main( String[] args ) { TestedBySample sample = new TestedBySample(); System.out.print(sample.add(1, 2)); } @TestedBy( testClass = "it.javalinux.testedby.TestedBySampleTest", testMethod = "addShouldWork" ) public int add( int i, int j ) { return i + j; } @TestedByList( {@TestedBy( testClass = "it.javalinux.testedby.TestedBySampleTest", testMethod = "addShouldWork" ), @TestedBy( testClass = "it.javalinux.testedby.TestedBySampleTest", testMethod = "addShouldWork2" )} ) public int add2( int i, int j ) { return i + j; } }
Oki, it’s nice, but can it really change your approach to unit tests? I think so.
How? At least in two different manner.
1. Design by interface and contracts
A famous adagio in software design says “Design by interface”. And it’s a sweet song for my ears. But of course you should “Test interface” too.
More formally “Design by interface” means defining interfaces of your API and then asking all implementors (maybe and a lot of times are different people or even companies) to implement these interfaces. Anyway all you can enforce is what a strongly typed language as Java can ensure: an interface implementation must respect its interface signature both in types and parameters. What you cannot enforce are behaviours. And of course it’s a big limitation in API design as this could drive to implementation with totally unpredictable behaviours.
Here comes to my mind Eiffell language and its design by contract (DbC) approach where contracts guide redefinitions of features in inheritance. There are a lot of tools providing DbC in Java, but my thought is that we already have a consolidated way to test contracts on methods and also finer behavior even of smaller piece of code: Unit tests.
With TestedBy, tests declared on an interface (or a superclass) can be run upon all implementers to verify they’re respecting the behaviour and contract defined in the super type. IOW the API designer not only provides the interfaces, but also a set of test classes verifying expected behaviour. Then every implementer would supply its own implementation and run the tests provided by the API designer against its concrete classes to verify they are respecting not only type safety but also beahviour/contract safety for which the API was designed. TesteBy here invokes a test defined for an interface passing to test class a concrete instance of class implementing the interface under test.
Here is an example on how this is achieved with TestedBy: we have added @TestedBy annotation on APIInterface and then provided a @BeforeTestedBy annotation to set the interface instance on which tests run. TestedBy will run shouldAddTwoAndThree both for APIImplOne and APIImplTwo, succeding on the first one and failing on the second
public interface APIInterface { @TestedBy( testClass = "it.javalinux.testedby.APITest", testMethod = "shouldAddTwoAndThree" ) public int add(int a, int b); } public class APIImplOne { public int add(int a, int b) { return a + b; } public class APIImplTwo { public int add(int a, int b) { return a - b; } public class APITest { private APIInterface instance; @BeforeTestedBy public beforeTestedBy(APIInterface instance) { this.instance = instance; } public void shouldAddTwoAndThree() { assertThat(instance.add(3,2), is(5)); } }
Of course I have kept the example simple, but TestedBy has some more annotations, for instance factory of classes under test can be specified when simple reflective invocation of no argument constructor isn’t enough.
2. Run test on current working class
You write a test to verify your code correctness. Moreover you are using unit test to ensure your changes isn’t breaking working code and mainly works of other people. How are you doing this? You make your changes and then you run all your tests against code you are modifying. Then at the end you run all tests to be sure you haven’t broke anything else.
What about having your IDE do the first step for you then? As it compile your modified classes it could (and IMHO it should) run tests against these classes. TestedBy makes this possible since you or your IDE could ever run tests against changed (aka compiled) classes. Here an eclipse plugin can do the magic of running tests insisting on you modified classes and verifing you aren’t breaking your test suite, not only your compilation, during code development. And this should not be too heavy, since the plugin could use TestedBy’s annotations to run only few tests insisting on your modified classes (or even methods).
Moreover running tests on a particular class under test you’ll get a clear report saying something like
"ClassUnderTest.methodUnderTest shouldThrowExceptionWithNullParameter doen't pass"or even better
"Failure: methodUnderTest in ClassUnderTest doesn't throw exception with null parameter, but it should!"Moreover you can of course totally break (if you like or need) conventions on class/method names and find, run and navigate your tests staring from your project classes.
Of course massive launches running all tests insisting on all classes will still be possible and your tests will be also runnable by JUnit since a TestedBy test is a JunitTest.
Some features
Well, let me detail some features I have in mind for TestedBy
- The first one maybe the most trivial, but one of the most useful: within your ide you can navigate sources starting from the class/method under test. This feature comes out of the box with Eclipse 4.4, since it makes full qualified class names navigable also if they are inside a string. Of course it’s just a starting point, and a specific Eclipse plugin may be developed to navigate the code, construct the tree of test classes from a class under test, execute tests insisting on the open/changed class (see point 3 too) and so on. I’m not an Eclipse guru, so any contributions on this area are more than welcome.
- You will find this annotation in you javadoc. Think how much advantage you can get if you are using a BDD approach, defining test methods like shouldNotAcceptNull() or shouldThrowsExceptionIfEmpty and so on. With a BDD approach you can in fact define and verify contracts and with TestedBy annotation in JavaDoc document it to your API users
- You can run test starting from class under test and not test class. I have already said a lot about this in the previous chapter.
-
A design by interface and contracts. I have already introduced this idea in a dedicated paragraph.
- Test class generation. A tool (ant, maven, or eclipse too) can use our TestedBy annotation to generate test classes. Read also point 6.
- Ant and/or maven task/plugin to run tests starting from class under test. This tools will manage also round tripping (it’s important to ensure test classes and annotations don’t go quickly out of sync):
- Reverse engeneering: starting from existing tests will add TestedBy annotations to class under test
- Running tests from TestedBy annotations will verify they run all tests and that no annotation point to not existing test.
- Generate (empty) test classes and methods starting from annotations.
Ok, that is not all, but I think it can give you the whole idea. I’m working on some other ideas behind this one, but I need to define them a little better before presenting them to the community.
Conclusions
So can this approach interest you? Let me known what you think about and share with your friends this post…more feedback I get, better refinement I can do.
Now the question is…which is the status of the project?
Well have a look to project homepage on javalinuxlabs. On googlecode you’ll find also some classes under svn. It’s not a really first implementation of the project…it’s more or less a proof of concept. But we are working hard to kick it out of the door soon.
Subscribe this feeds. I’ll make announcement here very very soon, and moreover other posts regarding evolutions of this idea.
Would you like to contribute? Have a look to this page to better understand in which area we need help.













18 Responses to “A new approach to unit tests”
First a couple of questions:
1) Wouldn’t it be better to eliminate the @TestedByList annotation and just recognize multiple @TestedBy annotations on a single method?
2) Why wouldn’t you just use the existing @Before annotation (from JUnit) in the APITest class above in lieu of the @BeforeTestedBy annotation?
I like this idea, and have thought about using similar annotations in the past, unrecognized by any tool, and enforced by nothing more than coding policies, to achieve this tie-in between methods and their tests. The problem was without the tooling, this level of “documentation” (since that’s all it is without the tooling) was ridiculously onerous. So I worry that although you’re talking about providing tooling necessary to eliminate much of the pain involved with specifying the detail in the @TestedBy annotations, you’re strongly tying the future maintenance of the code to an IDE that supports that tooling.
By JPAV on Oct 21, 2008
@JPAV
First of all thanks for the comment. To answer your question:
1) AFAIK it’s not possible to have more than one annotation of the same type on an alement. Maybe I’m missing something, have you any suggestion about?
2) I would leave @Before use to JUnit (we can have a before method setting something different than InterfaceInstance). I would support all JUnit (and maybe in future TestNG) feature and not use their methods/annotations for TestedBy specific feature. BeforeTestedBy create a specific instance of type implementing the interface and then you could use all JUnit feature to test against it. Naming is more than arguable…maybe @InjectClassUnderTest would be much better.
About tying maintenance of code to a specific IDE, I’m worried about too, but there are 3 points persuade me to go on:
And I think TestedBy could be really useful mainly for the ability to define test against interfaces.
a) If a tool is really useful it’s better to have than don’t have.
b) I would provide tools not only for IDE, but also for ant/maven for round tripping
c) Test written for TestedBy are plain Junit tests, so you can leave it whenever you want.
By Stefano MAESTRI on Oct 21, 2008
Really really nice. You’ll need some IDE support but I can see a pretty successful project here
By Jose Noheda on Oct 21, 2008
Nice post!
Focussing on behaviour instead of the formal (syntatic) contract of an interface, you may want to have a look at
http://gleichmann.wordpress.com/2007/12/02/why-interfaces-are-poor-contracts/
Personally, i thing DBC is far more powerfull, since your pre- and postconditions directly belong to the public signature of the interface and usually not only ‘tests’ for a limited set of test scenarios but present a more general statement of the forced behaviour.
With SpringContract for example, you’re also able to declare invariants, pre- and postconditions on Interfaces, which will be ‘enforced’ (checked) on all implementors.
For more info, you may want to take a look at
http://gleichmann.wordpress.com/2007/11/21/springcontracts-design-by-contract-with-seamless-integration-into-spring-is-now-open-source/
More, you can also tighten preconditions or weaken postconditions on subclasses. This concept isn’t given on a more test oriented concept (comparing DBC as a more general specification concept)
For further info you may take a look at
http://gleichmann.wordpress.com/2007/12/09/test-driven-development-and-design-by-contract-friend-or-foe/
Greetings
Mario
By Mario Gleichmann on Oct 21, 2008
Please note I’ve posted also a link to this post to TheServerSide.com and some comments has been posted there: http://www.theserverside.com/news/thread.tss?thread_id=51292
Anyway I’ll make soon a post here collecting all comments and moreover I’ll open a forum to discuss about testedby…blog doesn’t scale very well.
By Stefano MAESTRI on Oct 21, 2008
@Mario Gleichmann
Thanks for links to your great articles.
As said in previous post we will open soon a forum to discuss about TestedBy and I hope you will join us in these discussion, since seems to me you already put your mind on these arguments more than one time.
My only concern on your comment is about the sentence “More, you can also tighten preconditions or weaken postconditions on subclasses. This concept isn’t given on a more test oriented concept”. I’m not totally agree since a test defined on a super type define the thighest postcondition and weakest precondition that implementer have to match. Implementer could define test with tighten preconditions or weaken postconditions. What am I missing?
By Stefano MAESTRI on Oct 21, 2008
Stefano,
the idea to weaken preconditions and/or tighten postconditions is a core concept in DBC (i think there’s an excellent explanation about that topic in Bertrand Meyers Book ‘Eiffel’).
Shortly said, a subclass (as a kind of specialization of its superclass) should hold the Liszkov Substitution principle, in that clients that rely on the behaviour of the superclass (or an Interface) should also be able to work with subclasses. This is in full alignment with the above mentioned idea since you only minimize the oblifations of the client (the weakened preconditions) and may have stronger / better benefits (the postconditions, at least as strong as in the superclass, maybe stronger).
Plugging test cases to a superclass / Interface and maybe other testcases to a subclass / implementors isn’t the same, since it don’t reflect the mentioned principle.
Greetings
Mario
By Mario Gleichmann on Oct 22, 2008
Hi, just a quick remark:
> AFAIK it’s not possible to have more than one annotation of the same type on an element. Maybe I’m missing something, have you any suggestion about?
You could use array of annotations, in order to easily defined several testBy on one method.
See http://www.onjava.com/pub/a/onjava/2004/10/06/anno1.html
By VonC on Oct 23, 2008
@VonC
yep and it’s exactly what @TestedByList does: array of annotations inside another annotation.
By Stefano MAESTRI on Oct 23, 2008
I like the idea that one can specify near the code the tests that are expected to be run, and with ide/tool support that could be very powerful.
However two things occur to me after reading your post…
1) with the annotations, i know i would get pretty hacked off writing the testing class name over and over and over again….i dont know if its possible but if so wouldnt it be better to put a testedBy annotation on the class as well and list the test class there? then you wouldnt need to repeat it all the time but if you wanted too could override it in a specific method annotation.
This would of course restrict/ecourage you to put all the tests in one class, but most of us do that anyway dont we? and if you really wanted to you could still use the full anotation on each method….
2) With the interface tests and the running a test against each implementation, how do you envisage getting around the various constructors required for the different implementations?
Currently I specify an abstract class for each Interface, The test methods in it test the interfaces contract, then for each implementation i provide a test class that extends the abstract class… I assume this pattern is used by many of us.
The TestedBy annotations could still be applied to that and would even allow you to ensure that the test classes for an implementation actually extend the abstract test class.
Im still skeptical about the BeforeTestedBy anotation …after all its in the test class ( if i havent missunderstood ) In which case the annotation is then missleading …. Id also unclear if the APITest Class is a JUnit test? If not why not? and if it isnt why not use the Before annotation?
By Paul on Oct 24, 2008
@Paul
A lot of good points here. May I ask you to join our discussion group (link in my last post) to help us in our discussions.
Just very brief answer:
1) yep it will be possible to do that. I have already thought about.
2) As said in the article TestedBy will also provide annotations for testclass’ factories. And finally APITest it’s a JUnit class, but @Before can’t accept parameter. As said in another comment @BeforeTestedBy is a very bad name, maybe @InjectTestClassInstance (or somthing like it) would be much better
By Stefano MAESTRI on Oct 24, 2008
Interesting idea. Why annotate every method though? It clutters the code and makes it difficult to change a test hierarchy (you have to change all the class references in code which has nothing to do with the test hierarchy and then recompile that code). Why annotate classes at all? Why not just annotate the interface? The test will know exactly what methods should be tested. It will know the signature of each method so can test each method, without having to annotate every method in the implementing class. So you would do:
@TestedBy(testClass = …
public interface APIInterface {
but that’s too much hard coding for my liking. If you’re going to create a new testing framework, why not consider IOC? Allow the framework to inject the test hierarchy based on an abstraction?
package org.funnyfarm;
@TestedBy(testHierarchy = …
public interface APIInterface {
but you could refine it further by using the interface name as a lookup into a set of hierarchies registered with the framework. i.e. something along the lines of:
@TestedBy
public interface APIInterface {
that way you have one hook in the interface. No hooks in implementation code – APIInterfaceImpl implements APIInterface should be enough for the framework to test APIInterfaceImpl. Who cares what else APIInterfaceImpl does. It might implement another interface but the framework would know that from the signature and test accordingly.
By Alistair on Oct 27, 2008
sorry, just realised the xml might not show up in my comment!
<tests>
<test name=”org.funny.farm.APIInterface” class=”…”/>
<tests>
By Alistair on Oct 27, 2008
I really liked this post. Can I copy?
Thank you in advance.
Sincerely, Timur.
By TimurAlhimenkov on Jan 1, 2009
hi,
thanks,The article was very well written, very helpful to me
By purse on Jan 3, 2009
What do you mean exactly with “can I copy”?
Where do you want to copy it?
By Stefano MAESTRI on Jan 11, 2009