Saturday, February 7, 2009

Test Driven Development, BDD and Junit

Here is a question I asked on stackoverflow. The responses are pretty interesting.

I don't know if you read the recent post by Joel, but basically he says that you don't need unit testing for every unit or piece of code in your code base.
"And that strikes me as being just a little bit too doctrinaire about something that you may not need. Like, the whole idea of agile programming is not to do things before you need them, but to page-fault them in as needed." -- Joel
And I would say that most developers don't have full coverage anyway, yet there are some blogs out there touting the 100% code coverage.
"Yeah. In fact, if you're making any kind of API, a plug in API, it is very important to separate things into interfaces and be very very contractual, and tightly engineered" -- Joel

"Right. The longer I think about this, the less I care about code hygiene issues (laughs). Because the unit of measure that really matters to me is, how quickly you can respond to real business needs with your code." -- Jeff
On Kent Beck:

"It doesn't seem like you could actually get any code written if you're spending all your time writing 8,000,000 unit tests, and every single dinky little class that you need to split a URL into four parts becomes an engineering project worthy of making a bridge, where you spend six months defining 1000 little interfaces" -- Joel.

My question is this, do you agree with Joel? Do you spend a lot of time writing unit tests with full coverage.
He is my stance. I believe in more BDD, Behavior Driven Development. For me, I prototype a piece of code, establish some setup code and then execute a bunch of these driver routines. If something goes wrong, an exception or unexpected behavior then that is flagged as fail and otherwise a pass.

The junit style testing:

assertEquals(a, b);

Just seems so unnatural for a large majority of developer testing. Don't get me wrong, I can see where junit style testing is a good idea. But for a majority of business web applications. I can't imagine trying to retrofit a unit test to every piece of code in your application.

I was looking at scheme lisp implementations in Java. Scheme has a well known set of standards and a lot implementations attempt to be scheme compliant. So a junit test for scheme compliance is a good idea. You could do something like this.

assertEquals("3", "(+ 1 2)")

If it fails, the scheme implementation is not compliant, a very useful unit testing metric.
For something like a web application, you might have an action method 'process' that does a bunch of stuff. Connects to a database, sets up html form variables. On and on. How are you going to unit test that. You can and I am sure most will, but it will be so unnatural and won't keep up with the pace of the project. I can see where backend code (whatever it is) could be tested but the plug and play MVC web apps (think Spring MVC or Struts), I just don't see full test coverage, especially the junit kind.

Edited - To Charlie:

Let me get a little bit more specific. This is an example from wikipedia on unit testing, this is a good example and use of the 'junit' approach. The goal of the adder is to properly add two integers together. This might work as a good test of a interpreter.

public class TestAdder {
public void testSum() {
Adder adder = new AdderImpl();
assertTrue(adder.add(1, 1) == 2);
assertTrue(adder.add(1, 2) == 3);
assertTrue(adder.add(2, 2) == 4);
assertTrue(adder.add(0, 0) == 0);
assertTrue(adder.add(-1, -2) == -3);
assertTrue(adder.add(-1, 1) == 0);
assertTrue(adder.add(1234, 988) == 2222);

But, lets say you are writing a business web application. The ultimate goal is to display a user's bank account information to them. All bank transactions, their savings account information, their credit card info, render it to the browser.

Ideally, like the adder application, I guess you could do:

assertTrue(getPage() == displayedCorrectly())

I guess that is at a higher level of granularity. That 'getPage' might include database calls to the bank account table. Or invoking the server page and sending over the form values. Invoking the web framework and make sure the right parameters are established. Etc, etc. What 'unit' do you test in that case? And I read some TDD articles where you normally are not supposed to write tests that includes code where you make a remote connection (like a database server), write to a file, etc.

Well? if you can't connect to the bank database because you hard coded the wrong database name, seems like a very valid test to check for?

I am not implying anything, these are just points where I am confused on how to about unit testing for more complex apps.

There were some good responses:
"Behaviour Driven Development (BDD) is about specifying the behaviour of the system by writing exectable specifications in the form of tests. When there is a thing that the code does not yet do, but it should do, then you write a test/spec for that thing, which in turn helps you to design code that passes the specification. For example to specify a ball, you might have specs (test methods) such as "the ball is round", "when nobody touches the ball then it stays in place", "when somebody hits the ball then it moves in the opposite direction", "when the ball hits the floor then it bounces" etc." -- Esko Luontola

"More on TDD, test-driven design. It can be a little confusing, because it requires a really different way of thinking about the low-level coding process; that's why I prefer to think of it as an extensional definition of a specification rather than "design." ("Extensional" in the sense of an extensional vs intentional definition of a set.)

"We're used to thinking of unit testing as "code then test." With TDD, it's really "test, then code." You write a test for code that doesn't yet exist; by doing so you define a specific bit of the behavior of the code. For a real bumpo example of this, let's code a routine that tests a number for being an even number. You start knowing the definition of an even number, ie, n is even if n ≡ 0 mod 2." -- Charlie Martin

No comments: