TestNG 支持两种不同类型的依赖注入:本地(由 TestNG 本身执行)和外部(由 Guice 等依赖注入框架执行)。
本机依赖注入
TestNG 允许您在方法中声明其他参数。发生这种情况时,TestNG 会自动用正确的值填充这些参数。依赖注入可以用在以下地方:
- 任何 @Before 方法或 @Test 方法都可以声明ITestContext类型的参数。
- 任何 @AfterMethod 方法都可以声明ITestResult类型的参数,这将反映刚刚运行的测试方法的结果。
- 任何@Before 和@After 方法(@BeforeSuite 和@AfterSuite 除外)都可以声明 XmlTest 类型的参数,其中包含当前的
标记。 - 任何@BeforeMethod(和@AfterMethod)都可以声明 java.lang.reflect.Method类型的参数。此参数将接收此 @BeforeMethod 完成后(或在为 @AfterMethod 运行的方法之后)将调用的测试方法。
- 任何 @BeforeMethod 都可以声明Object[]类型的参数。此参数将接收即将提供给即将到来的测试方法的参数列表,这些参数可以由 TestNG 注入,例如java.lang.reflect.Method或来自@DataProvider。
- 任何 @DataProvider 都可以声明ITestContext或java.lang.reflect.Method类型的参数 。后一个参数将接收即将被调用的测试方法。
您可以使用@NoInjection注解关闭注入:
public class NoInjectionTest {
@DataProvider(name = "provider")
public Object[][] provide() throws Exception {
return new Object[][] { { CC.class.getMethod("f") } };
}
@Test(dataProvider = "provider")
public void withoutInjection(@NoInjection Method m) {
Assert.assertEquals(m.getName(), "f");
}
@Test(dataProvider = "provider")
public void withInjection(Method m) {
Assert.assertEquals(m.getName(), "withInjection");
}
}
下表总结了可以为各种 TestNG 注解本地注入的参数类型:
Guice 依赖注入
如果您使用 Guice,TestNG 为您提供了一种使用 Guice 模块注入测试对象的简单方法:
@Guice(modules = GuiceExampleModule.class)
public class GuiceTest extends SimpleBaseTest {
@Inject
ISingleton m_singleton;
@Test
public void singletonShouldWork() {
m_singleton.doSomething();
}
}
在这个例子中,GuiceExampleModule应该将接口ISingleton绑定到某个具体的类:
public class GuiceExampleModule implements Module {
@Override
public void configure(Binder binder) {
binder.bind(ISingleton.class).to(ExampleSingleton.class).in(Singleton.class);
}
}
如果您需要更灵活地指定应该使用哪些模块来实例化您的测试类,您可以指定一个模块工厂:
@Guice(moduleFactory = ModuleFactory.class)
public class GuiceModuleFactoryTest {
@Inject
ISingleton m_singleton;
@Test
public void singletonShouldWork() {
m_singleton.doSomething();
}
}
模块工厂需要实现接口IModuleFactory:
public interface IModuleFactory {
/**
* @param context The current test context
* @param testClass The test class
*
* @return The Guice module that should be used to get an instance of this
* test class.
*/
Module createModule(ITestContext context, Class<?> testClass);
}
您的工厂将被传递一个测试上下文的实例和 TestNG 需要实例化的测试类。你的createModule方法应该返回一个 Guice 模块,它知道如何实例化这个测试类。您可以使用测试上下文来查找有关您的环境的更多信息,例如在testng.xml中指定的参数等……您将获得更多的灵活性和 Guice 功能,使用parent-module和guice-stage套件参数。 guice-stage允许您选择用于创建父注入器的阶段。默认值为DEVELOPMENT。其他允许的值为PRODUCTION和TOOL. 以下是在 test.xml 文件中定义父模块的方法:
<suite parent-module="com.example.SuiteParenModule" guice-stage="PRODUCTION">
</suite>
TestNG 只会为给定的套件创建一次此模块。还将使用此模块来获取测试特定 Guice 模块和模块工厂的实例,然后将为每个测试类创建子注入器。使用这种方法,您可以在父模块中声明所有常见绑定,也可以在模块和模块工厂中注入在父模块中声明的绑定。这是此功能的示例:
package com.example;
public class ParentModule extends AbstractModule {
@Override
protected void conigure() {
bind(MyService.class).toProvider(MyServiceProvider.class);
bind(MyContext.class).to(MyContextImpl.class).in(Singleton.class);
}
}
public class TestModule extends AbstractModule {
private final MyContext myContext;
@Inject
TestModule(MyContext myContext) {
this.myContext = myContext
}
@Override
protected void configure() {
bind(MySession.class).toInstance(myContext.getSession());
}
}
<suite parent-module="com.example.ParentModule">
</suite>
package com.example;
@Test
@Guice(modules = TestModule.class)
public class TestClass {
@Inject
MyService myService;
@Inject
MySession mySession;
public void testServiceWithSession() {
myService.serve(mySession);
}
}
如您所见,ParentModule 声明了 MyService 和 MyContext 类的绑定。然后使用构造函数注入将 MyContext 注入到 TestModule 类中,该类也为 MySession 声明绑定。然后将测试 XML 文件中的 parent-module 设置为 ParentModule 类,这可以在 TestModule 中进行注入。稍后在 TestClass 中,您会看到两个注入: MyService - 来自 ParentModule 的绑定 MySession - 来自 TestModule 的绑定 此配置可确保您在此套件中的所有测试都将使用相同的会话实例运行,MyContextImpl 对象每个套件仅创建一次,这使您可以为套件中的所有测试配置通用环境状态。