Disclaimer: I'm a developper, and, as such, I'm lazy. So please don't expect this blog to be updated very often. Moreover, most of the time I don't really know what I'm talking about (it's not like software development is my job or something), so don't expect to find very useful stuff here either. Basically it's just some sort of semi public notepad with a few problems / things I encountered. Oh, and I'm French, so my english wont be any good... That said, nice reading!

Enforcing architecture rules with a unit test…

… is neat!

At the beginning of a project, everybody in the team agrees to make things properly, identify layers, isolate responsibilities, code with defined formatting rules, etc. But after a few weeks, rules tend to be bent and laxism is all over the place. Formatting rules can be checked at code-time using Checkstyle Eclipse plugin, test coverage can be monitored using Maven Cobertura plugin (build-time) or EclEmma Eclispe plugin (test-time), but what can one use to enforce a certain set of architecture rules?

yum, multi-layered cake!

You use a unit test that triggers a Checkstyle “importControl” module of course! Okay, you will use the Checkstyle API, so you need the checkstyle JAR on your classpath. The unit test will initialize an audit context, referencing a configuration file that has to be on the classpath too :


public class CheckstyleIntegrationTest {

	class Listener implements AuditListener {

		public List<AuditEvent> errorList = new ArrayList<AuditEvent>();

		public void addError(AuditEvent arg0) {
			errorList.add(arg0);
		}

		public void addException(AuditEvent arg0, Throwable arg1) {
			throw new RuntimeException("Error while executing checksyle", arg1);
		}

		...

	}

	@SuppressWarnings("unchecked")
	private List<File> getList(String... directories) {
		List<File> l = new ArrayList<File>();
		for (String s : directories) {
			l.addAll(FileUtils.listFiles(new File(s), new String[] { "java" }, true));
		}
		return l;
	}

	@Test
	public void check() throws Exception {
		InputStream is = getClass().getResourceAsStream("/checkstyle.xml");
		PropertyResolver propertyResolver = new PropertyResolver() {

			public String resolve(String arg0) throws CheckstyleException {
				return null;
			}

		};
		Configuration configuration = ConfigurationLoader.loadConfiguration(is, propertyResolver, false);
		Checker checker = new Checker();
		checker.setModuleClassLoader(getClass().getClassLoader());
		checker.configure(configuration);
		Listener listener = new Listener();
		checker.addListener(listener);
		checker.process(getList("src/main/java", "src/mock/java"));
		for (AuditEvent auditEvent : listener.errorList) {
			System.err.println("Problem in file " + auditEvent.getFileName() + " : " + auditEvent.getMessage());
		}
		Assert.assertEquals(0, listener.errorList.size());
	}

}

Then in your checkstyle.xml configuration file :


<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
    "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
    "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">

<module name="Checker">
	<module name="TreeWalker">
		<module name="ImportControl">
			<property name="file" value="src/test/resources/import-control.xml" />
		</module>
	</module>
</module>

And the only thing left is to specify your rules, for example :


<?xml version="1.0"?>
<!DOCTYPE import-control PUBLIC
    "-//Puppy Crawl//DTD Import Control 1.0//EN"
    "http://www.puppycrawl.com/dtds/import_control_1_0.dtd">

<import-control pkg="com.mycompany">
	<subpackage name="services">
		<!-- common packages -->
		<allow pkg="java" />
		<allow pkg="javax" />
		<allow pkg="org.springframework" />
		<allow pkg="org.apache.commons" />
		<allow pkg="org.apache.log4j" />

		<!-- application layers -->
		<allow pkg="com.mycompany.dao" />
		<allow pkg="com.mycompany.model" />

etc ....

Comments are closed.