2012-08-06 12 views
5

Obecnie pracuję nad narzędziem do monitorowania wykorzystującym aspektj. Ponieważ to narzędzie powinno być niezależne od technologii (o ile to możliwe), nie używam Springa do iniekcji. Ale chcę, aby moje aspekty zostały przetestowane na podstawie testów jednostkowych.Jak wyśmiać aspekt

przykład Aspekt: ​​

@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; 
    } 
} 

Jednak ja naprawdę nie wiem, jak skonstruować testów jednostkowych (pole repozytorium powinno być wyśmiewany (używając Mockito)), ale nie mam tworzenia obrazu pod kontrolą, stąd Nie mogę ustawić zależności ręcznie. Co mam wywołać, aby uzyskać instancję? Lub jest jakiś inny scenariusz, jak testować aspekty aspektu jednostkowego.

Dzięki.

Odpowiedz

2

Mówisz znaleźć swój własny sposób na wprowadzenie atrapa obiektu hacky. Czego dokładnie nie lubisz i jak sobie to wyobrażasz? Mogę tylko zgadywać:

Czy nie podoba ci się, że globalnie zastępujesz połączenia na OwlApiRepository.getInstance() w swoim aspekcie meta? Następnie można wyraźnie ograniczyć mock zastrzyk obiektu do konstruktora Aspect (używam natywnej składni AspectJ bo czuję się nieswojo z POJO adnotacji stylu):

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(); 
    } 
} 

Jak można również zobaczyć, ten wariant meta (aspekt testowanie) ma również przełącznik do włączania i wyłączania w dowolnym momencie. Może to też było coś, czego nie lubiłeś. Jak już powiedziałem, zgaduję. Po Twojej opinii mogę być w stanie odpowiedzieć dokładniej.

Edit: Co do twoich obaw, myślę, że zwrócił się do nich w miarę możliwości:

  • Nie trzeba mock uchwyt.

  • Aspekt można (de) aktywować. Łatwiej byłoby uzależnić jego aktywację od innych warunków, dlatego jest aktywna tylko w środowisku testowym. Jeśli to nadal nie wystarcza, użyj tasowania kompilacji dla aspektów produkcyjnych i tkania czasu obciążenia dla twojego aspektu testowego. W ten sposób jego kod bajtowy nie będzie nawet obecny w twoim środowisku produkcyjnym.

  • Moja wersja globalnie nie zastępuje niczego, ale jak dobry chirurg tylko tnie w minimalnie inwazyjny sposób dokładnie w jednym miejscu.

  • naprawdę nie mogę zrozumieć swoje zaniepokojenie manipulacji kodu bajtowego z kilku powodów: używać AspectJ, czyli z natury manipulacji kodu bajtowego (tkactwo). Używasz Mockito, który tworzy klasy w czasie wykonywania. Również nie rozumiem, gdzie widzisz niedobór AspectJ. Nie wyjaśniłeś, w jaki sposób chcesz zachować "standardowe środki języka" lub jaki interfejs powinien zapewnić testowanie. Nawet jeśli tak, nie mogę zmienić języka (AJ) i narzędzia (Mockito) według własnego wyboru.

+0

Co mi się nie podoba w moim roztwór: globalne zastąpienie statycznej metody sprawia, że ​​trudno przetestować - muszę ręcznie zresetować repozytorium makiety (zamiast ustawiania nowego). Musiałem także wprowadzić fałszywego właściciela, aby uzyskać dostęp do fałszywego obiektu repozytorium. Trzecią rzeczą jest to, że nie lubię temperować z bajtowym kodem, aby ustawić próbę, naprawdę myślę, że to musi być zrobione standardowymi środkami języka (jeśli nie jest to możliwe, to imho pokazuje niedobór aspektu projekt). Ale z kodu, twoje rozwiązanie może (przynajmniej nie potrzebuje posiadacza) :-). – malejpavouk

+0

Punkt wzięty, nagroda jest twoja. Dzięki :-) – malejpavouk

+0

Przepraszamy za hałas. Postanowiłem dodać komentarze do mojej odpowiedzi, ponieważ liczba znaków dostępnych dla komentarzy była zbyt mała. Ale dzięki za nagrodzenie mnie nagrodą. :) – kriegaex

1

możesz podzielić swoje testy. najpierw przetestuj logikę aspektu. to pojo. możesz go przetestować, jak chcesz. Druga część to testowanie punktów. w tym przypadku utwórz inny, prosty aspekt z tymi samymi punktami (np. wyodrębnij je jako stałą). Może są jakieś dedykowane narzędzia do testowania, ale nie jestem świadomy każdy i to najprostszy sposób, aby przyjść do mojego umysłu

1

Moje obecne rozwiązanie jest wprowadzenie tego AspectJ siekać, aby zastąpić metodę singletons fabrycznego

@Aspect 
public class MockingAspect { 

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

Co powiesz na coś takiego, w zasadzie nadal zachowuj swój aspekt, wewnątrz aspektu deleguj zachowanie do innego interfejsu i naśmiewaj się z tego interfejsu dla twoich testów, zamiast drwić z samego aspektu. Oto pseudokod:

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; 
    } 
} 

Teraz w teście można to zrobić:

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

Chcesz tylko testów jednostkowych? To jest niewielki test jednostkowy do testowania niestandardowej adnotacji z aspektami służącymi do zawijania pola w wyjątku aplikacji niestandardowej. (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); 
}