2015-07-09 372 views
2

我目前正在爲使用Spring和AspectJ的應用程序中的類編寫JUnit單元測試。被測試的類有一些公共方法,這些方法在方面類中通過around-advice方法建議。 Aspect有兩個被注入的字段,即使我已經在測試應用程序上下文中成功實例化了這些bean,並且在調用它們的方法時,它們會拋出nullpointerexceptions,但在執行建議的方法時會變爲null。下面的代碼的簡化版本:單元測試@Around建議的方法

類來進行測試:

public class ClassUnderTest { 

    @Inject 
    private Foo foo; 

    @Audit(StringValue="arg", booleanValue=true) 
    public Object advisedMethod() { 
     Object ret = new Object(); 
     //things happen 
     return ret; 
    } 

的看點:

@Aspect 
@Configurable 
public class AuditAspect implements Versionable { 

    @Inject 
    Foo foo; 

    @Inject 
    Bar bar; 

    @Around("@annotation(Audit)") 
    public Object aroundAdvice(ProceedingJoinPoint pjp, Audit audit) { 

     // Things happen 
     privMethod(arg); 
     // Yet other things happen 
     Object ret = pjp.proceed(); 
     // Still more things happen 
     return ret; 
    } 

    private Object privMethod(Object arg) { 
     // Things continue to happen. 
     // Then this throws a NullPointerException because bar is null. 
     bar.publicBarMethod(arg2); 
     // Method continues ... 
    } 
} 

審計接口:

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Audit { 
    String value() default ""; 
    boolean bool() default false; 
} 

的配置文件提供應用上下文:

import static org.mockito.Mockito.*; 

@Configuration 
public class ClassUnderTestTestConfig { 

    @Bean 
    Foo foo() { 
     return mock(Foo.class); 
    } 

    @Bean 
    Bar bar() { 
     return mock(Bar.class); 
    } 

    @Bean 
    ClassUnderTest classUnderTest() { 
     return new ClassUnderTest(); 
    } 

    @Bean 
    @DependsOn({"foo", "bar"}) 
    Aspect aspect() { 
     return new Aspect(); 
    } 
} 

測試類:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextHierarchy({ 
    @ContextConfiguration(classes = ClassUnderTestTestConfiguration.class), 
    @ContextConfiguration(classes = ClassUnderTest.class) 
}) 
public class ClassUnderTestTest { 

    private static final String sessionNumber = "123456"; 

    @Inject 
    Foo foo; 

    @Inject 
    Bar bar; 

    @Inject 
    ClassUnderTest classUnderTest; 

    @Inject 
    Aspect aspect; 

    @Test 
    public void test() { 
     // This call triggers the advice which ends up throwning 
     // a NullPointerException. 
     classUnderTest.advised(); 
    } 
} 

我也試着做我自己的春天代理,然後通過將此代碼添加到測試類手動添加方面它建議在this stack overflow post,:

@Before 
public void setUp() { 
    DataPointRestWebService target = new DataPointRestWebService(); 
    AspectJProxyFactory proxyMaker = new AspectJProxyFactory(target); 
    proxyMaker.addAspect(auditAspect); 
    dataPointRestWebService = proxyMaker.getProxy(); 
} 

不過是結束了投擲:

AopConfigException: Advice must be declared inside an aspect type. Offending method 'public java.lang.Object Aspect.aroundAdvice' in class [Aspect] 

我鰭d這個神祕的東西,因爲我的Aspect類在它之前確實有@Aspect註解,並且類在測試環境之外工作。

我對Spring和AspectJ非常陌生,所以我完全接受我對這一切都錯誤的看法。我在這裏提供了虛擬代碼,希望能夠省略無用的細節,但也因爲工作代碼是專有的,而不是我的。如果您認爲我遺漏了一個重要細節,請告訴我,我會嘗試添加它。

在此先感謝您的任何幫助,請讓我知道我是否遺漏了任何重要信息。

編輯:

根據要求,我已經添加了全NullPointerException異常堆棧跟蹤:

java.lang.NullPointerException 
    at com.unifiedcontrol.aspect.AuditAspect.getCaller(AuditAspect.java:265) 
    at com.unifiedcontrol.aspect.AuditAspect.ajc$inlineAccessMethod$com_unifiedcontrol_aspect_AuditAspect$com_unifiedcontrol_aspect_AuditAspect$getCaller(AuditAspect.java:1) 
    at com.unifiedcontrol.aspect.AuditAspect.aroundAuditAdvice(AuditAspect.java:79) 
    at com.unifiedcontrol.server.rest.DataPointRestWebServiceTest.dummyTest(DataPointRestWebServiceTest.java:109) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) 
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) 
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 

凡AuditAspect ==看點和DataPointRestWebService == ClassUnderTest。

+0

你的例子對我來說很好(沒有'@ Before'方法)。你能發佈完整的NPE堆棧跟蹤嗎? –

+0

剛添加它。感謝您抽出寶貴的時間。 –

+0

看起來你在'AuditAspect'中忽略了一些重要的東西。你能提供一個完整的和可重複的例子,即。一個[MCVE](http://stackoverflow.com/help/mcve)? –

回答

1

這裏是最終解決我們的問題:

@Bean 
@DependsOn({"foo", "bar"}) 
Aspect aspect() { 
    return new Aspect(); 
} 

到:

我們在ClassUnderTestTestConfig類改變了以下方法

@Bean 
@DependsOn({"foo", "bar"}) 
Aspect aspect() { 
    return Aspects.aspectOf(Aspect.class); 
} 

爲此我們增加了下面的import語句:

import org.aspectj.lang.Aspects; 

原始代碼成功地返回了一個新的Aspect對象,但是當調用ClassUnderTest.advisedMethod()時,該連接點被委派給一個不是使用非空foo和bar成員注入的不同Aspect對象。有關Aspects.aspectOf()方法如何工作的信息可確保由TestConfig創建的Aspect對象能夠爲adviceMethod()的調用提供建議。

目前我不知道爲什麼這解決了這個問題。其他人在工作中找到了解決方案。我計劃研究並編輯這篇文章以獲得更多信息,但同時也歡迎所有貢獻。

+0

我在修改我的方面時碰到過這個問題。以前,我被要求手動編織方面到這個類,像這樣: 'AspectJProxyFactory factory = new AspectJProxyFactory(service);' 'factory.addAspect(new Aspect());' 'service = factory.getProxy() ; – Sinker