Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using JMockit and Spring AOP together

Suppose I have a program that looks like this:

@Component
public class MainAction {
    public void doTheAction() {
        System.out.println("Now doing the action");
    }
}

@Aspect
@Component
public class BeforeAspect {
    @Autowired
    private Logger logger;

    @Before("execution(* thepackagename.MainAction.*(..))")
    public void doBefore() {
        logger.log("The @Before advice has run");
    }
}

@Component
public class Logger {
    public void log(String s) {
        System.out.println(s);
    }
}

This is working fine if I run it through Eclipse (the main method esentially calls mainAction.doTheAction() after mainAction is created by Spring).

Now I want to write a test that ensures that the log method is called correctly when doTheAction is called. We're using JMockit for our testing. (This is a very simplified case of a problem I'm actually facing; a more complex logger is being called via an AOP aspect, and the wrong value of something is being logged. Before working on a fix, I'm trying write a test to ensure the logged value is correct.)

This is what my (simplified) test currently looks like:

@RunWith(JMockit.class)
@ContextConfiguration(locations = {"classpath:Beans.xml"})
public class MainActionTest {
    @Tested
    private MainAction mainAction;

    @Test
    public void testThatLoggerIsCalled(@Injectable Logger logger) {
        new Expectations() { {
            logger.log(anyString);
        } };
        mainAction.doTheAction();
    }
}

The @ContextConfiguration may be useless. Earlier I had tried @RunWith(SpringJunit4ClassRunner.class), which is why @ContextConfiguration is there, but none of the mocking stuff was handled. Also, I'm using @Tested and @Injectable instead of @Autowired and @Mocked, following the suggestion in this question; without that, mainAction remained null. So now the test runs, and Now doing the action appears in the output. But The @Before advice has run doesn't appear (and doesn't appear even if I don't mock the Logger), and the expectation fails.

How can I use JMockit and AOP together?

Edit: As requested, I added something to print the classpath property. Here it is (with unimportant parts of some path names removed):

Eclipse workspaces\springtest8\target\test-classes
Eclipse workspaces\springtest8\target\classes
C:\eclipse\plugins\org.junit_4.11.0.v201303080030\junit.jar
C:\eclipse\plugins\org.hamcrest.core_1.3.0.v201303031735.jar
.m2\repository\org\jmockit\jmockit\1.18\jmockit-1.18.jar
.m2\repository\junit\junit\4.11\junit-4.11.jar
.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar
.m2\repository\org\springframework\spring-context\4.2.0.RELEASE\spring-context-4.2.0.RELEASE.jar
.m2\repository\org\springframework\spring-aop\4.2.0.RELEASE\spring-aop-4.2.0.RELEASE.jar
.m2\repository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar
.m2\repository\org\springframework\spring-beans\4.2.0.RELEASE\spring-beans-4.2.0.RELEASE.jar
.m2\repository\org\springframework\spring-core\4.2.0.RELEASE\spring-core-4.2.0.RELEASE.jar
.m2\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar
.m2\repository\org\springframework\spring-expression\4.2.0.RELEASE\spring-expression-4.2.0.RELEASE.jar
.m2\repository\org\aspectj\aspectjrt\1.8.6\aspectjrt-1.8.6.jar
.m2\repository\org\aspectj\aspectjweaver\1.8.6\aspectjweaver-1.8.6.jar
.m2\repository\org\springframework\spring-test\4.2.0.RELEASE\spring-test-4.2.0.RELEASE.jar
.m2\repository\javax\inject\javax.inject\1\javax.inject-1.jar
/C:/eclipse/configuration/org.eclipse.osgi/bundles/201/1/.cp/
/C:/eclipse/configuration/org.eclipse.osgi/bundles/200/1/.cp/

Edit 2: I got things to work by removing JUnit4 from the Libraries tab in Configure Build Path.

like image 527
ajb Avatar asked Aug 16 '15 00:08

ajb


1 Answers

The following test works fine, using Spring 3.0 or newer (tested with Spring 3.0.7, 4.0.5, and 4.2.0):

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
public class MainActionTest
{
    @Inject MainAction mainAction;

    @Test
    public void testThatLoggerIsCalled(@Mocked final Logger logger)
    {
        mainAction.doTheAction();

        new Verifications() {{ logger.log(anyString); }};
    }
}
like image 159
Rogério Avatar answered Oct 11 '22 00:10

Rogério