2011-10-05 1 views
1

J'ai une interface dans un framework de test unitaire: CDIMocker. J'utilise actuellement un intercepteur pour permettre de se moquer dans un conteneur CDI. C'est une expérience - l'une des deux approches de tests unitaires que je considère. (L'autre candidat principal est d'utiliser un constructeur et une méthode d'injection pour tous les beans et un test unitaire en dehors du CDI - auquel cas ce travail devient plus un exercice d'apprentissage dans CDI Extensions).Extensions CDI: Puis-je exposer une interface dans deux portées?

J'ai deux étendues personnalisées - TestClassScoped et TestMethodScoped. Mon coureur personnalisé JUnit4 encapsule les blocs Classe et Méthode appropriés dans les instructions qui démarrent et arrêtent ces étendues selon les besoins. Il démarre également une instance de Weld-SE si nécessaire. Il sait si c'est dans CDI parce que l'extension se souvient.

L'interface moqueuse est la même partout où elle est utilisée. Il serait bon de l'exposer dans les deux domaines, donc je pourrais

// Sadly Static Injection currently doesn't work, but if it did 
@Inject @TestClassScoped 
private static CdiMocker s_classScopedMocker 

@Inject @TestMethodScoped 
private CdiMocker m_methodScopedMocker 

Il existe d'autres moyens évidents. J'ai actuellement une méthode d'usine sur un singleton en dehors de CDI qui peut retourner l'une de ces instances (ThreadLocal), ou en créer une nouvelle de courte durée. J'ai également eu du succès en créant deux classes concrètes et en leur indiquant différentes portées.

J'ai essayé d'utiliser des méthodes de production annotées comme ci-dessus, mais pas de chance. Peut-être une simple erreur, peut-être un malentendu.

@Produces @TestClassScoped 
public CdiMocker getClassScopedMockerForCdi() 
{ 
    return getTestClassContext().getMocker(); 
} 

@Produces @TestMethodScoped 
public CdiMocker getMethodScopedMockerForCdi() 
{ 
    return getTestMethodContext().getMocker(); 
} 

je pensais d'une certaine partie de la documentation de l'IDC, il était possible de déclarer portées sur des points d'injection comme je l'ai fait, mais je note que l'instance <> interface ne me permet pas de sélectionner() en utilisant l'annotation scope alors peut-être que c'est faux.

Je pourrais fournir deux qualificatifs. Une annotation peut-elle être qualificative et étendue en même temps?

Une autre idée serait d'avoir mon extension fournir deux Bean <CdiMocker>, les deux exposant la même classe, mais dans des portées différentes. Ils peuvent également fournir des fonctions create() et destroy() car les instances de CdiMocker sont gérées par mes deux Contexts personnalisés. L'impression que j'ai de CDI est qu'une Classe donnée ne peut vivre que dans un Scope, alors serait-ce Wrong?

Des suggestions sur ce qui est le meilleur?

Merci - Richard

(je serais ravi d'ouvrir la source du résultat, mais ai fait assez dans le temps de travail que je dois demander si peu probable L'argument des affaires serait un examen public-je utiliser.. un Interceptor maintenant avec l'inconvénient qu'il doit être laissé en place, mais je me demande si je pourrais réaliser quelque chose en intercepter le cycle de vie du bean dans l'extension.Nous pouvons utiliser Alternatives pour des choses comme la couche de communication qui parle à notre ancien serveur d'applications, mais pour certaines choses, un test unitaire nécessite une simulation personnalisée et les alternatives sont trop globales.)

Répondre

1

J'ai créé

@Qualifier 
@Target({TYPE, METHOD, PARAMETER, FIELD}) 
@Retention(RUNTIME) 
@Documented 
public @interface Scoped 
{ 
    Class<? extends Annotation> value(); 
} 

J'ai actuellement deux implémentations de Bean. Les parties pertinentes (inhabituelles) sont:

/** 
* A Bean<> implementation for the CdiMocker beans 
*/ 
class MockerBean implements Bean<CdiMocker> 
{ 
    private final class ScopedAnnotation extends AnnotationLiteral<Scoped> implements Scoped 
    { 
     private static final long serialVersionUID = 1L; 
     public Class<? extends Annotation> value() { return m_context.getScope(); } 
    } 

    private final CdiMockContextControl m_context; 

    public MockerBean(CdiMockContextControl context) 
    { 
      m_context = context; 
    } 

La classe du bean est CdiMocker.La classe

@Override 
    public Class<?> getBeanClass() 
    { 
      return CdiMocker.class; 
    } 

Les qualificatifs incluent mon ScopedAnnotation défini ci-dessus. J'ai également inclus Default et Any. Peut-être que je dois supprimer ces?

La portée est renvoyée par mon interface CdiMockContextControl.

@Override 
public Class<? extends Annotation> getScope() 
{ 
    return m_context.getScope(); 
} 

type est mon interface CdiMocker

@Override 
public Set<Type> getTypes() 
{ 
    Set<Type> types = new HashSet<Type>(); 
    types.add(CdiMocker.class); 
    types.add(Object.class); 
    return types; 
} 

Parce que le cycle de vie est géré ailleurs que je retourne l'existant.

@Override 
public CdiMocker create(CreationalContext<CdiMocker> arg0) 
{ 
    return m_context.getMocker(); 
} 

... et ne le détruisez pas. La solution utilise des qualificatifs, donc je suppose que c'est maintenant "correct". Je suppose que je peux utiliser la gestion du cycle de vie de cette façon?

Ma classe de test (que mon Runner instancier en utilisant CDI) a

/** 
* My CDI Extension makes a Mocking Context available in the Test Method Scope. 
* This will be created before every test method, then destroyed afterwards. 
*/ 
@Inject @Scoped(TestMethodScoped.class) 
private CdiMocker m_testMethodMocker; 
  • Richard
Questions connexes