Eclipse and TDD
It’s been after quite a while that I’m back to doing eclipse plugins. In this span of time I learnt some good techniques — TDD. It’s a really good safety check to know that your code is in a working state.
Test-first-development or TDD as it is called is where you generally write tests first and then write code that makes these tests pass. This in some ways ensures that you write clean APIs that are simple to test, use and understand. The APIs don’t really do more that what is needed for the job. So far so good.
The fun began when I moved back to doing eclipse. I now realize that it is difficult to do TDD when I’m writing code that implements (or extends) some third party interfaces. These implementations return some more interfaces, which only compounds to the problem of test-first-development.
The way I write eclipse plugins is create a null implementation that does nothing at all. It merely prints method names of the methods being called on to the console. I then start off eclipse (in debug mode), understand the sequence in which the interface is invoked, and what eclipse does with the return values (that’s walking though a lot of code, BTW), I also sometimes need to look at similar implementation to understand what they do. I then gradually implement those methods in a way that what eclipse expects. This gives another meaning to BDD — breakpoint driven development.
This process is what most developers would probably do when using a framework for the first time. Most frameworks (unlike the eclipse frameworks) are much simpler than eclipse is. There are some known interfaces that are exposed. Since there are very few interfaces to implement, developers gradually get to know them very well. As the expectation out of these interfaces is known well, gradual use of these interfaces, makes it easy to write tests for the interface before implementing the interface itself, which means that developers can move from BDD to TDD.
This process is a quick (and IMO, dirty) way of writing code. The initial code is not written with testability in mind, writing unit tests at the end is quite difficult, and testability can be achieved after quite some bit of refactoring to allow make the code testable.
An example of this is a typical java webapp that consists of some servlets, and jsps containing tag-libs etc. There are very few known interfaces that the clients implement (the HttpServlet, Filter, FilterChain etc.) Since the webapp framework itself is very compact, a framework to unit test the implementations of interfaces exposed by the servlet spec has evolved over a period of time. Cactus is one such example:
Cactus is a simple test framework for unit testing server-side java code (Servlets, EJBs, Tag Libs, Filters, …) [...] It uses JUnit and extends it.
To test a servlet one would write a test as:
public class TestSampleServlet extends ServletTestCase {
public void testThatServletPrintsHelloWorld(){
Servlet servlet = new HelloWorldServlet();
...
session.setAttribute("name", "bob");
...
assertTrue("hello, bob", servlet.printHelloWorld());
}
}
It is mind boggling to imagine a framework similar to cactus to test the thousands of interfaces exposed by a framework like eclipse, and we are still not talking about the other, “smaller” projects under the eclipse umbrella.
Given this scenario, how do you do test-first-development ?
You ask “Given this scenario, how do you do test-first-development ?” but your real question is “Given a project with millions of lines of code that wasn’t developed with TDD, how do I go back and make it all TDD retroactively?”
And the answer is: You don’t.
You can’t, for exactly the reasons you’re stating.
What you do is you START using TDD going forward. Next time you make a change – and pick a small one – write tests for that. And you don’t have to write tests for every interface it calls; in fact, you *shouldn’t*. Assume they have their own tests, and that they work as they’re supposed to. You want to test *your* code, and make sure that – assuming its dependencies are working correctly – your code itself works correctly too.
Now, as you get more advanced, you’ll want to start stubbing out some of those interfaces (for speed) or mocking them (for verifiability). In fact, for a lot of people, mocking is the best way to make sure that your code does what it’s supposed to do. But there’s a lot of gray in there – how do I mock without being totally brittle and implementation-dependent – and you may want to wait until you’re more comfortable with the “me-them” boundaries before starting to use mocks.
Another good way to start using TDD is for bug fixes. Any time you get a bug report, don’t just fix the bug; create a test that would fail if the bug existed. Run it; it’ll fail. (If it doesn’t, your test is wrong, and you’ve learned something.) Then fix the bug, your test will pass, and now you can be sure that there won’t be any regressions on that bug, ever.
Jay Levitt
13 Oct 07 at 11:00 pm
Jay:
I do not have millions of lines of code as yet, I’ve just started in a humble way
I understand that given the way I’m working as of now, there’s NO way that I can do TDD, and I’m perfectly willing to accept this for a fact.
Indeed that’s what I’d do on normal project where I myself define the API contract. What about a case where to make up a system like an eclipse plugin, you end up incrementally creating a bunch of implementations of interfaces that are needed by eclipse ? There’s very little margin for tests, since the “expectations” are not really known when the code is written, and what happens is that one ends up refactoring code to make it testable, by injecting some mocks into them.
I think I need to describe the problem better.
Ketan
13 Oct 07 at 11:18 pm
[...] This is in continuation to my previous post “Eclipse and TDD” [...]
Ketan Padegaonkar’s Blog » Blog Archive » Eclipse and TDD (Part 2)
14 Oct 07 at 12:21 am