The Artima Developer Community
Sponsored Link

Let's Reconsider That
Frustration-Driven Language Development
by Michael Feathers
April 6, 2004
Summary
There are an incredible number of programming languages out there. What would it be like if we had a few with features that really supported testing?

Advertisement

I just noticed that Brian Marick had an interesting response to something I said in my last blog entry. I was talking about how difficult it is to instantiate classes outside of their applications. He mentioned that it would be great to have a tool that would allow you to create an arbitrary object and poke at it, test it etc, regardless of how many dependencies it has. You could supply the tool with mocks for the bad dependencies and it would use the mocks. That would be useful.

Brian's post reminded me of something I ended up making a while ago, a little framework I called Ghostworld. It was a tool for that sort of thing. I ditched it because I discovered that PicoContainer has every feature I added and quite a few more. The world doesn't need any more duplicate projects so Ghostworld remains unreleased. But, there is another reason why I never released Ghostworld. I felt it was incomplete. It was missing the killer feature. Let me tell you about it.

To make an arbitrary class you fill up the Ghostworld with types:

Ghostworld world = new Ghostworld();
world.addImposter("Profile", UserProfile.class);
...

Then you ask it to create an object:

Reservation res = (Reservation)world.makeObject("Reservation");

The ghostworld walks the constructors of Reservation and attempts to create one using the classes it knows about. So far, so good. PicoContainer does this also, but what I really wanted was a framework that lets me do this:

class Reservation
{
	private AIGConnection connection;
	
	public Reservation(..) {
		connection = new AIGConnection(..);
	}
}

world.addImposter("AIGConnection", TestingAIGConnection.class);
Reservation res = (Reservation)world.makeObject("Reservation");

Now, here's what I want to have happen. Every time that that Reservation object or one of its subobjects creates a AIGConnection using a 'new' expression, I want a TestingAIGConnection object to be created instead. In the example above, we'd now have a reservation which holds onto an instance of TestingAIGConnection rather than AIGConnection. Wouldn't that be useful? We could instantiate and test nearly any piece of code without refactoring. I'm not convinced that is great thing. Abused it would just let code get uglier without bound. But, used well, it could really help people out of same jams.

Unfortunately, I never had time to really dig in and find out whether there is some sort of classloader, AOP, or bytecode manipulation magic that would allow me to substitute classes over and over again at runtime. But, the thing that I have been reflecting about is the fact that there seems to be a lot of energy behind efforts like this. And when that happens it usually means that language support is something to consider.

I forget whether it was Paul Graham or Richard Gabriel (or someone else entirely) who said something like this: "the algorithm for developing every "next" language is simple.. it has to be more dynamic than its predecessor and it has to look like C." So, let's imagine a language like that.

class Account
{
	private balance = 0;
	private log = new TransactionLog();
	
	deposit(value) {
		log.addEntry(balance, value);
		balance += value;
	}
	...
}

(By the way, just in case you haven't seen it before, there's actually a language with syntax close to this. It's called Groovy).

The Account class is simple enough, but let's suppose we want to test it, and let's make it a real bear. The TransactionLog class talks to a database behind the scenes. Every time you add an entry, it's sloooowww.

The way languages are today, if we want to get past this, we have to add a constructor that accepts a log, or introduce a factory method and override it, or.. well there are a bunch of solutions, but what if we had a language which let us to do this?

test Account
{
	class TransactionLog
	{
		entries = new ArrayList();
		addEntry(entry) {
			entries.add(entry);
		}
	}

	testDeposit {
		a = new Account();
		a.deposit(3);
		assertEquals(3, a.getBalance());
		assertEquals(1, a.log.entries.size());
	}
}

The language would allow us to define classes inside a test class. The classes we define in tests supersede classes outside the test. So, whenever some object creates a transaction log in this test class, they get an instance of this simple replacement class that doesn't touch a database.

Another feature... the tests have access to all private data.

Could these features be abused? Yes. If you can test without breaking dependencies it's easier to create snarls of complicated code. On the other hand, it would be much easier to get out of them too.

What do you think? Isn't it about time languages had first-class support for testing?

Talk Back!

Have an opinion? Readers have already posted 11 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Michael Feathers adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Michael has been active in the XP community for the past five years, balancing his time between working with, training, and coaching various teams around the world. Prior to joining Object Mentor, Michael designed a proprietary programming language and wrote a compiler for it, he also designed a large multi-platform class library and a framework for instrumentation control. When he isn't engaged with a team, he spends most of this time investigating ways of altering design over time in codebases.

This weblog entry is Copyright © 2004 Michael Feathers. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use