我在单元测试框架中有一个接口:CDIMocker。我目前正在使用拦截器来允许在CDI容器中进行模拟。这是一个实验 - 我正在考虑的几种单元测试方法之一。 (另一个主要竞争者是对所有bean使用构造函数和方法注入以及CDI之外的单元测试 - 在这种情况下,这项工作在CDI扩展中变得更加学习了。)
我有两个自定义范围 - TestClassScoped和TestMethodScoped。我的JUnit4自定义运行器在根据需要启动和停止这些范围的语句中包装适当的类和方法块。如果需要,它还会启动Weld-SE的实例。它知道它是否在CDI中,因为扩展会记住。
无论我们在哪里使用,模拟器界面都是相同的。在两个范围中公开它会很好,所以我可以
// Sadly Static Injection currently doesn't work, but if it did
@Inject @TestClassScoped
private static CdiMocker s_classScopedMocker
@Inject @TestMethodScoped
private CdiMocker m_methodScopedMocker
还有其他明显的方法。我目前在CDI外部的单例上有一个工厂方法,它可以返回这些实例中的任何一个(ThreadLocal),或创建一个新的短期实例。我也成功创建了两个具体的类,并在它们上声明了不同的范围。
我尝试使用如上所述注释的制作人方法,但没有运气。也许是一个简单的错误,也许是一种误解。
@Produces @TestClassScoped
public CdiMocker getClassScopedMockerForCdi()
{
return getTestClassContext().getMocker();
}
@Produces @TestMethodScoped
public CdiMocker getMethodScopedMockerForCdi()
{
return getTestMethodContext().getMocker();
}
我认为从CDI文档的某些部分可以像我一样在注入点上声明范围,但我注意到Instance<>接口不允许我使用作用域注释选择(),这可能是错误的。
我可以提供两个限定词。注释可以同时成为限定符和范围吗?
另一个想法是让我的扩展提供两个Bean< CdiMocker>,它们都暴露相同的类,但是在不同的范围内。他们还可以提供自定义的create()和destroy(),因为CdiMocker实例由我的两个自定义上下文管理。我得到的CDI的印象是,一个给定的类只能存在于一个范围内,所以这是错的吗?
关于什么是最好的建议?
由于 - 理查德
(我喜欢开源的结果,但是我已经做了足够多的工作时间我不得不这么做。业务论点是公开评论。我现在使用Interceptor,它的缺点是它有留下原因,但想知道我是否可以通过拦截扩展中的bean生命周期来实现某些目标。我们可以使用Alternatives来处理与我们的遗留应用服务器对话的通信层,但是对于某些事情,单个单元测试需要一个自定义模拟和替代品过于全球化。)
答案 0 :(得分:1)
我已经创建了
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface Scoped
{
Class<? extends Annotation> value();
}
我目前有两个Bean实现。相关(不寻常)部分是:
/**
* 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;
}
bean类是CdiMocker.class
@Override
public Class<?> getBeanClass()
{
return CdiMocker.class;
}
限定符包括我在上面定义的ScopedAnnotation。我还包括Default和Any。也许我需要删除这些?
范围由我的CdiMockContextControl接口返回。
@Override
public Class<? extends Annotation> getScope()
{
return m_context.getScope();
}
Type是我的CdiMocker界面
@Override
public Set<Type> getTypes()
{
Set<Type> types = new HashSet<Type>();
types.add(CdiMocker.class);
types.add(Object.class);
return types;
}
因为生命周期是在其他地方管理的,所以我会返回现有的生命周期。
@Override
public CdiMocker create(CreationalContext<CdiMocker> arg0)
{
return m_context.getMocker();
}
......不要破坏它。
@Override
public void destroy(CdiMocker instance, CreationalContext<CdiMocker> ctx)
{
// It is managed by the Context, so I must not destroy it here.
ctx.release();
}
解决方案是使用限定符,所以我认为它现在是“正确的”。我想我可以用这种方式使用生命周期管理吗?
我的测试类(我的Runner使用CDI实例化)
/**
* 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;