java 如何在生产中使用 CDI 测试类时注入模拟
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32422930/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to inject mocks while testing classes using CDI in production
提问by Matthias Wimmer
I am programming in a Java SE environment using WELD-SE for dependency injection. Therefore dependencies of a class look something like this:
我正在 Java SE 环境中使用 WELD-SE 进行依赖注入编程。因此,类的依赖项看起来像这样:
public class ProductionCodeClass {
@Inject
private DependencyClass dependency;
}
When writing a unit test for this class I am creating a mock for DependencyClass
and as I don't want to start a complete CDI environment for every test I run, I "inject" the mock manually:
在为这个类编写单元测试时,我正在创建一个模拟DependencyClass
,因为我不想为我运行的每个测试启动一个完整的 CDI 环境,我手动“注入”模拟:
import static TestSupport.setField;
import static org.mockito.Mockito.*;
public class ProductionCodeClassTest {
@Before
public void setUp() {
mockedDependency = mock(DependencyClass.class);
testedInstance = new ProductionCodeClass();
setField(testedInstance, "dependency", mockedDependency);
}
}
The statically imported method setField()
I have written myself in a class with tools I use in testing:
setField()
我在一个类中使用我在测试中使用的工具编写的静态导入方法:
public class TestSupport {
public static void setField(
final Object instance,
final String field,
final Object value) {
try {
for (Class classIterator = instance.getClass();
classIterator != null;
classIterator = classIterator.getSuperclass()) {
try {
final Field declaredField =
classIterator.getDeclaredField(field);
declaredField.setAccessible(true);
declaredField.set(instance, value);
return;
} catch (final NoSuchFieldException nsfe) {
// ignored, we'll try the parent
}
}
throw new NoSuchFieldException(
String.format(
"Field '%s' not found in %s",
field,
instance));
} catch (final RuntimeException re) {
throw re;
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
}
What I don't like about this solution is, that I need this helper over and over in any new project. I already packaged it as a Maven project I can add as a test dependency to my projects.
我不喜欢这个解决方案的一点是,我在任何新项目中都一遍又一遍地需要这个助手。我已经将它打包为一个 Maven 项目,我可以将其作为测试依赖项添加到我的项目中。
But isn't there something ready made in some other common library I am missing? Any comments on my way of doing this in general?
但是,我缺少的其他一些常用库中没有现成的东西吗?对我这样做的一般方式有什么评论吗?
回答by Magnilex
Mockito supports this out of the box:
Mockito 支持开箱即用:
public class ProductionCodeClassTest {
@Mock
private DependencyClass dependency;
@InjectMocks
private ProductionCodeClass testedInstance;
@Before
public void setUp() {
testedInstance = new ProductionCodeClass();
MockitoAnnotations.initMocks(this);
}
}
The @InjectMocks
annotation will trigger injection of classes or interfaces mocked in the test class, in this case DependencyClass
:
@InjectMocks
注释将触发测试类中模拟的类或接口的注入,在这种情况下DependencyClass
:
Mockito tries to inject by type (using name in case types are the same). Mockito does not throw anything when injection fails - you will have to satisfy the dependencies manually.
Mockito 尝试按类型注入(在类型相同的情况下使用名称)。当注入失败时,Mockito 不会抛出任何东西——你必须手动满足依赖关系。
Here, I am also using the @Mock
annotation instead of calling mock()
. You could still use mock()
, but I prefer using annotations.
在这里,我也使用@Mock
注释而不是调用mock()
. 您仍然可以使用mock()
,但我更喜欢使用注释。
As a side note, there are reflection tools available, which supports the functionality you implemented in TestSupport
. One such example is ReflectionTestUtils
.
作为旁注,有可用的反射工具,它们支持您在TestSupport
. 一个这样的例子是ReflectionTestUtils
。
Perhaps better still is to use constructor injection:
也许更好的是使用构造函数注入:
public class ProductionCodeClass {
private final DependencyClass dependency;
@Inject
public ProductionCodeClass(DependencyClass dependency) {
this.dependency = dependency;
}
}
The main advantage here is that it is clear what classes the class depends on, and that it cannot easily be constructed without providing all the dependencies. Also, it allows the injected class to be final.
这里的主要优点是类所依赖的类很清楚,并且在不提供所有依赖项的情况下无法轻松构建它。此外,它允许注入的类是最终的。
By doing this, @InjectMocks
is not necessary. Instead, just create the class by providing the mock as a parameter to the constructor:
通过这样做,@InjectMocks
是没有必要的。相反,只需通过将模拟作为参数提供给构造函数来创建类:
public class ProductionCodeClassTest {
@Mock
private DependencyClass dependency;
private ProductionCodeClass testedInstance;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
testedInstance = new ProductionCodeClass(dependency);
}
}
回答by Jan Galinski
Alternative when mockitos build in functions do not suffice: Try needle4j.org
当 mockitos 内置函数不够时的替代方法:试试needle4j.org
It's a injection/mock framework that allows injection of mocks and concrete instances and also supports postConstruct for lifecycle simulation.
它是一个注入/模拟框架,允许注入模拟和具体实例,还支持 postConstruct 进行生命周期模拟。
public class ProductionCodeClassTest {
@Rule
public final NeedleRule needle = new NeedleRule();
// will create productionCodeClass and inject mocks by default
@ObjectUnderTest(postConstruct=true)
private ProductionCodeClass testedInstance;
// this will automatically be a mock
@Inject
private AServiceProductionCodeClassDependsOn serviceMock;
// this will be injected into ObjectUnderTest
@InjectIntoMany
private ThisIsAnotherDependencyOfProdcutionCodeClass realObject = new ThisIsAnotherDependencyOfProdcutionCodeClass ();
@Test
public void test_stuff() {
....
}
}