如何在生产环境中使用CDI测试类时注入mock



我在Java SE环境中使用WELD-SE进行依赖注入编程。因此,类的依赖关系看起来像这样:

public class ProductionCodeClass {
    @Inject
    private DependencyClass dependency;
}

在为这个类编写单元测试时,我为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);
    }
}

静态导入的方法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);
        }
    }
}

我不喜欢这个解决方案的地方是,我在任何新项目中都需要反复使用这个助手。我已经将它打包为一个Maven项目,我可以将其作为测试依赖项添加到我的项目中。

但是不是有一些东西已经在其他公共图书馆我错过了吗?对我一般的做法有什么意见吗?

Mockito开箱即用:

public class ProductionCodeClassTest {
    @Mock
    private DependencyClass dependency;
    @InjectMocks
    private ProductionCodeClass testedInstance;
    @Before
    public void setUp() {
        testedInstance = new ProductionCodeClass();
        MockitoAnnotations.initMocks(this);
    }
}

@InjectMocks注释将触发在测试类中模拟的类或接口的注入,在本例中DependencyClass:

Mockito尝试按类型注入(在大小写类型相同的情况下使用name)。当注入失败时,Mockito不会抛出任何东西——你必须手动满足依赖项。

在这里,我也使用@Mock注释而不是调用mock()。您仍然可以使用mock(),但我更喜欢使用注释。

作为旁注,有可用的反射工具,它们支持您在TestSupport中实现的功能。ReflectionTestUtils就是这样一个例子。


也许使用构造函数注入更好:

public class ProductionCodeClass {
    private final DependencyClass dependency;
    @Inject
    public ProductionCodeClass(DependencyClass dependency) {
        this.dependency = dependency;
    }
}

这里的主要优点是,它清楚地显示了类所依赖的类,并且如果不提供所有依赖项,就不容易构造它。而且,它允许被注入的类是final的。

这样做,@InjectMocks是不必要的。相反,只需通过将mock作为参数提供给构造函数来创建类:

public class ProductionCodeClassTest {
    @Mock
    private DependencyClass dependency;
    private ProductionCodeClass testedInstance;
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        testedInstance = new ProductionCodeClass(dependency);
    }
}

当mockitos内置函数不够时,可以选择:尝试needle4j.org

它是一个注入/模拟框架,允许注入模拟和具体实例,还支持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() {
         ....
    }
}

相关内容

  • 没有找到相关文章

最新更新