2012-08-06 8 views
5

현재 aspectj를 사용하는 몇 가지 모니터링 도구로 작업 중입니다. 이 도구는 기술에 독립적 (가능한 멀리)해야하기 때문에 Spring을 주입에 사용하지 않습니다. 그러나 나는 나의 면모를 단위 테스트하기를 원한다.어 스펙트를 조롱하는 방법

화면 예 :

@Aspect 
public class ClassLoadAspect { 
    private Repository repository; 

    public ClassLoadAspect() { 
     repository = OwlApiRepository.getInstance(); 
    } 

    @After("anyStaticInitialization()") 
    public void processStaticInitilization(JoinPoint jp) { 
     Class type = jp.getSourceLocation().getWithinType(); 
     if (type.isInterface()) { 
      repository.storeInterfaceInitialization(type); 
     } else if (type.isEnum()) { 
      repository.storeEnumInitialization(type); 
     } else { 
      repository.storeClassInitialization(type); 
     } 

    } 

    @Pointcut("staticinitialization(*) && !within(cz.cvut.kbss.odra..*)") 
    public void anyStaticInitialization() { 
    } 

    public Repository getRepository() { 
     return repository; 
    } 

    public void setRepository(Repository repository) { 
     this.repository = repository; 
    } 
} 

그러나, 그러므로, 정말 단위 테스트 (저장소 필드가 조롱한다 (사용 mockito))를 구성하는 방법, 잘 모릅니다,하지만 통제 측면 생성이없는 종속성을 수동으로 설정할 수 없습니다. 인스턴스를 가져 오려면 어떻게해야합니까? 또는 aspectj 측면의 단위 테스트 방법에 대한 다른 시나리오가 있습니다.

감사합니다.

답변

2

당신은 해키 mock 객체를 소개하는 자신의 방법을 찾을 말을 여기에 의사입니다. 당신은 정확히 무엇을 싫어하며 어떻게 상상합니까? 나는 단지 추측 할 수 있습니다 :

당신은 당신의 메타 관점에서 OwlApiRepository.getInstance()에 대한 호출을 세계적으로 대체한다는 사실에 싫은가요? 그럼 당신은 구체적으로 (필자는 POJO 주석 스타일 불편 때문에 네이티브 AspectJ의 구문을 사용하고 있습니다) 화면의 생성자에 mock 객체 주입을 제한 할 수 있습니다 :

public privileged aspect ClassLoadTestAspect { 
    static boolean active = true; 

    declare precedence : ClassLoadTestAspect, ClassLoadAspect; 
    pointcut classLoadAspect() : 
     if(active) && 
     withincode(ClassLoadAspect.new()) && 
     call(* OwlApiRepository.getInstance()); 

    Object around() : classLoadAspect() { 
     return new MockRepository(); 
    } 
} 

당신도 볼 수 있듯이, 메타 (측면의 변형을 테스트) 측면에는 마음대로 스위치를 켜고 끌 수있는 스위치가 있습니다. 아마도 이것은 당신이 싫어하는 것일 수도 있습니다. 내가 말했듯이, 나는 추측하고있다. 귀하의 의견을 듣고 좀 더 구체적으로 대답 할 수있을 것입니다.

편집 : 귀하의 우려에 대해서는, 나는 가능한 한 멀리를 해결 한 생각 :

  • 당신은 모의 홀더가 필요하지 않습니다.

  • 양상을 활성화 할 수 있습니다. 다른 조건에 따라 정품 인증을 쉽게 할 수 있으므로 테스트 환경에서만 활성화됩니다. 그래도 충분하지 않은 경우 프로덕션 측면에 컴파일 시간을 사용하고 테스트 측면에 대해서는로드 타임을 사용하십시오. 이렇게하면 바이트 코드가 프로덕션 환경에 존재하지 않게됩니다.

  • 내 버전이 전 세계적으로 대체하는 것은 아니지만 훌륭한 외과 의사처럼 한 곳에서만 정확하게 침습성이 떨어집니다.

  • 여러 가지 이유로 바이트 코드 조작에 대한 우려를 정말로 이해할 수 없습니다. AspectJ, 즉 본질적으로 바이트 코드 조작 (제직)을 사용합니다. 런타임에 클래스를 생성하는 Mockito를 사용합니다. 또한 AspectJ의 결함을 어디에서 보는지 이해하지 못한다. "표준 언어 수단"이 어떻게 동작하는지 또는 테스트를 위해 제공해야하는 인터페이스를 원하는 방식으로 설명하지 않았습니다. 당신이 가지고 있었다고하더라도, 나는 당신을 위해 당신이 선택한 언어 (AJ)와 도구 (Mockito)를 바꿀 수 없다.

+0

내 솔루션 싫어하는 것 : 정적 메소드의 글로벌 교체은 (는) 자기 테스트 할 수 있습니다 - (대신 새로운 하나를 설정하는) 내가 수동으로 저장소 모의를 다시 설정해야합니다. 또한 모의 저장소 개체에 액세스하기 위해 모의 홀더를 도입해야했습니다. 세 번째 것은 내가 모의 수식을 설정하기 위해 바이트 코드로 숙성하기를 좋아하지 않는다는 것입니다. 이것은 언어의 표준 방법으로 수행되어야한다고 생각합니다. (가능하지 않으면 imho는 aspectj의 결함을 보여줍니다. 디자인). 그러나 코드에서 솔루션이 수행 할 수도 있습니다 (적어도 보유자는 필요하지 않습니다) .-). – malejpavouk

+0

찍은 포인트, 현상금은 당신 것입니다. 감사합니다 :-) – malejpavouk

+0

죄송합니다. 주석에 사용할 수있는 문자 수가 너무 적기 때문에 내 대답에 주석을 추가하기로 결정했습니다. 하지만 어쨌든 현상금 덕분에 감사드립니다. :) – kriegaex

1

테스트를 나눌 수 있습니다. 우선 aspect의 논리를 테스트해라. 그것은 pojo입니다. 당신이 원하는대로 테스트 할 수 있습니다. 두 번째 부분은 pointcut를 테스트하는 것입니다. 이 경우 동일한 포인트 컷 (예 : 상수로 추출)을 사용하여 또 다른 간단한 애스펙트를 만듭니다. 어쩌면이 일부 전용 테스트 도구입니다하지만 난 어떤 잘 모르는 것 같아요 그것은 내 마음

1

내 현재 솔루션이 AspectJ를가 싱글 팩토리 메소드를 오버라이드 (override)하기 위해 해킹 소개에 와서 가장 쉬운 방법이었다

@Aspect 
public class MockingAspect { 

    @Around("call(synchronized static OwlApiRepository *(..))") 
    public OwlApiRepository processGetInstance(ProceedingJoinPoint jp) {  
     System.out.println("getting mock"); 
     return MockHolder.getMock(); 
    } 
} 
0

어떨까요?이 선을 따라 무엇인가 어떨까요, 기본적으로 애스펙트를 유지하고, 애스펙트 내부는 애스펙트 자체를 조롱하는 대신 다른 인터페이스에 비헤이비어를 위임하고 테스트를 위해 해당 인터페이스를 모의합니다.

public interface ClassLoadHelper{ 
    void processStaticInitialization(Class<?> clazz); 
} 

public class ClassLoadHelperImpl implements ClassLoadHelper{ 
    private Repository repository; 

    public ClassLoadHelperImpl() { 
     repository = OwlApiRepository.getInstance(); 
    } 

    void processStaticInitialization(Class<?> clazz){ 
     if (type.isInterface()) { 
      this.repository.storeInterfaceInitialization(type); 
     } else if (type.isEnum()) { 
      this.repository.storeEnumInitialization(type); 
     } else { 
      this.repository.storeClassInitialization(type); 
     }   
    } 
} 


@Aspect 
public class ClassLoadAspect { 
    private ClassLoadHelper classLoadHelper; 


    @After("anyStaticInitialization()") 
    public void processStaticInitilization(JoinPoint jp) { 
     Class<?> type = jp.getSourceLocation().getWithinType(); 
     this.classLoadHelper.processStaticInitialization(type); 

    } 

    @Pointcut("staticinitialization(*) && !within(cz.cvut.kbss.odra..*)") 
    public void anyStaticInitialization() { 
    } 

    public ClassLoadHelper getClassLoadHelper() { 
     return classLoadHelper; 
    } 

    public void setClassLoadHelper(ClassLoadHelper classLoadHelper) { 
     this.classLoadHelper = classLoadHelper; 
    } 
} 

지금 테스트에서이 작업을 수행 할 수 있습니다 :

ClassLoadAspect.aspectOf().setClassLoadHelper(mockClassLoadHelper); 
1

그냥 단위 테스트를 원하십니까? 이것은 사용자 정의 응용 프로그램 예외에서 throwable을 래핑하는 목적으로 aspect를 사용하여 사용자 정의 주석을 테스트하기위한 작은 단위 테스트입니다. (TestNG를 + Mockito)

public class ResourceApplicationExceptionAspectTest { 
@Mock 
private ProceedingJoinPoint pjp; 
@Mock 
private ResourceApplicationException resourceApplicationException; //annotation definition 

@BeforeMethod 
public void setUp() throws Exception { 
    MockitoAnnotations.initMocks(this); 

} 

@Test(groups ="unit", expectedExceptions = ResourceApplicationException.class) 
public void testWrapExceptionAdvice() throws Throwable { 

    ResourceApplicationExceptionAspect aspect = new ResourceApplicationExceptionAspect(); 

    when(pjp.proceed()).thenThrow(new NullPointerException()); 
    aspect.wrapExceptionAdvice(pjp, resourceApplicationException); 
} 
관련 문제