I have several AspectJ policy enforcement aspects that are applied to my Maven src/main/java directory. Recently, some holes in these aspects have been discovered, so I would like to create unit tests for them.
What I'd like to do is create Java files in the test directory (which is not compiled by AspectJ), then programmatically invoke the AspectJ compiler on selected Files and making assertions based on the outcome.
Something like this would be perfect:
assertThat("MyJavaClass.java", producesCompilerErrorFor("SomeAspect.aj"));
has anybody done anything similar?
As usual, here's my own answer:
I created a class called AbstractAspectJPolicyEnforcementTest
. Some of the contents are proprietary information, but I'll show you the most important stuff:
protected Matcher<File> producesCompilationErrorWith(final File aspectFile) {
return new AspectJCompilationMatcher(aspectFile, Result.ERROR);
}
private class AspectJCompilationMatcher extends TypeSafeMatcher<File> {
private final File aspectFile;
private final Result expectedResult;
private Result result;
public AspectJCompilationMatcher(final File aspectFile, final Result expectedResult) {
this.aspectFile = aspectFile;
this.expectedResult = expectedResult;
}
@Override
protected boolean matchesSafely(final File javaSourceFile) {
result = compile(javaSourceFile, aspectFile);
return result == expectedResult;
}
@Override
public void describeTo(final Description description) {
description.appendText("compilation result: ").appendValue(result);
}
}
enum Result {
ERROR,
WARNING,
SUCCESS
}
private Result compile(final File javaFileName, final File aspectFile) {
assertExists(javaFileName);
assertExists(aspectFile);
List<String> argList = newArrayList();
// java 7 compatibility
argList.add("-source");
argList.add("1.7");
argList.add("-target");
argList.add("1.7");
// set class path
argList.add("-cp");
argList.add(System.getProperty("java.class.path"));
// add java file
argList.add(javaFileName.getAbsolutePath());
// add aspect files
argList.add(aspectFile.getAbsolutePath());
for (File additionalAspectFile : requiredAspects) {
assertExists(additionalAspectFile);
argList.add(additionalAspectFile.getAbsolutePath());
}
String[] args = argList.toArray(new String[argList.size()]);
List<String> fails = newArrayList();
List<String> errors = newArrayList();
List<String> warnings = newArrayList();
List<String> infos = newArrayList();
// org.aspectj.tools.ajc.Main;
Main.bareMain(args, false, fails, errors, warnings, infos);
if (!fails.isEmpty() || !errors.isEmpty()) {
return Result.ERROR;
} else if (!warnings.isEmpty()) {
return Result.WARNING;
} else {
return Result.SUCCESS;
}
}
And here's how I use it in a test class:
public class ForbiddenPackageNameAspectTest extends AbstractAspectJPolicyEnforcementTest {
@Test
public void testBadPackageName() throws Exception {
assertThat(sourceFile(BadJavaClass.class),
producesCompilationErrorWith(findAspect("ForbiddenPackageNameAspect")));
}
@Test
public void testGoodPackageName() throws Exception {
assertThat(sourceFile(ForbiddenPackageNameAspectTest.class),
compilesWithoutWarningWith(findAspect("ForbiddenPackageNameAspect")));
}
}
Of course in a next step, I could extend this to allow to check for specific error messages, but for the beginning this will do.
I wrote a framework to isolate each aspect using Mocks and verify pointcut matching. It works with real or fictive methods calls and execution.
Maybe it might help you.
https://github.com/mock4aj/mock4aj
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With