Berichten met label acceptance testing

FitNesse and OSGi

As a demonstrator for a customer, I recently built a set of fixtures that allow FitNesse acceptance tests to talk to an OSGi framework. This code is by no means production quality, but merely intended to show the concept and explain the challenges.

I will not explain the details of the acceptance tests here, however, if there’s one point I would like to get across, it’s your fixtures should be as narrow as possible to easily accommodate for implementation changes. Study the different UserAdmin fixtures for more details. Also, I assume some familiarity with OSGi.

FitNesse and OSGi. Why?

Of course its fun, there is some real benefit to be gained here. While the industry well understands the need for unit- and integration testing, also in a modular context, it becomes more complex to create the necessary link between business and code. Yes, using a modular architecture we can behave in a more agile fashion, but all that agility is no good if the business doesn’t hop on the train, and explain well what it needs. FitNesse allows the business to explain its goals in business-lingo, while forcing the specification to be precise enough to be executable: if a concept cannot be explained by simple scenarios, something is wrong, but that’s a different story.

The modular nature of OSGi means that behavior of an application is more emergent than deterministic, making it harder to reason about its correctness: we can prove that our code and bundles are correct (unit tests), that everything works together as it should (integration tests), and that it looks right (user interface tests). However, proving that the business rules (which may well be one of those emergent properties) are handled correctly in a given setting, is another can of worms: we need to connect our acceptance tests to the OSGi framework.

The big picture

FitNesse and OSGi - overview

The solution presented below uses a special ‘fixtures’ bundle, which can be deployed along side other bundles in your framework. This bundles exposes an interface (in our case, through an HTTPServlet), which is used by a set of connectors, which in turn are used by FitNesse.

The details

FitNesse and OSGi - detailed

The ingredients are two parts connector code, one part boiler plate, and one part genuine OSGi-aware fixtures.

The connectors

Starting at the level closest to FitNesse, we find a set of fixtures that FitNesse can use. For us, these contain merely boiler plate code.

public void removeUser(String name) throws Exception {
    doRemoteCall(buildRemoteCall("UserAdmin", name), Void.class);
}

This code instructs our RemoteInvoker to do some call to the outside world. For more details, see RemoteInvoker.java in the UserAdminRemoteFixtures project.

The fixture bundle

Moving one step closer to our service, and into the OSGi framework, we find a FixtureServlet, whose task it is to receive calls from the RemoteInvoker, and turn them into actual method calls on the fixtures.

The fixtures, then, are almost regular OSGi aware objects. I chose to use the Apache Felix Dependency Manager for the dependency management of the fixtures. So, for our UserAdmin fixture, the dependencies are

manager.add(createService()
    .setInterface(UserAdminListener.class.getName(), null)
    .setImplementation(userAdmin)
    .add(createServiceDependency()
        .setService(UserAdmin.class)
        .setRequired(true)));

Here, we state that we have some instance of a fixture userAdmin that registers itself as a UserAdminListener and needs a UserAdmin. How straightforward is that?

The final step takes us to the actual fixture,

public class UserAdminFixture implements UserAdminListener {
	private volatile UserAdmin m_userAdmin;
...
	public void addUser(String name) {
		m_usersCreatedInLastCall = 0;
		m_userAdmin.createRole(name, Role.USER);
	}
...
}

which is just another component using a the UserAdmin service.

Putting it all together

All we now need to do is deploy the fixture bundle in our project, and instruct FitNesse to use the remote connector. The zip file at the bottom of this post contains two shell scripts to do exactly that.

Future work

As I stated at the top of this story, this is by no means production quality code, but the concepts stand as they are. Given the way FitNesse works, the connectors do not need much extra work, perhaps support for collections. However, we could use

  • a way to reduce the boiler plate code,
  • a way to ensure that that both side of the fixtures use the same function naming, and
  • better integration, for instance by only firing up a framework once a FitNesse suite is started.

Let’s play with it!

I have built a zip file containing everything you need to get started, including a set of scenarios that can run with both a homebrew implementation of a User Admin, and the actual Apache Felix User Admin. A Readme gives you more information on getting it all up and running.

, , , ,

1 reactie