18 依赖注入
TestNG支持两种不同的注入方法:原生方法(
native,有TestNG执行)和扩展方法(由依赖的注入的框架执行,如Guice)
18.1原生注入方法
TestNG运行在方法种声明额外的参数。如果声明额外参数,TestNG会自动填充这些参数。依赖注入可用于如下地方:
- 任何@Before 或者@Test方法可以声明ITestContext类的参数
- 任何@AfterMethod的方法可以声明ITestResult类参数,这参数反馈了刚才运行测试方法的测试结果
- 任何@Before和@After方法可以声明XmlTest类参数,这类参数包含当前<Test>标签
- 任何@BeforeMethod(和@AfterMethod)可以声明java.lang.reflect.Method类参数。这个参数可以接受@BeforeMethod运行完之后调用的测试方法(or after the method as run for @AfterMethod)。
- 任何@BeforeMethod可以声明Object[]类别的参数。这个参数接受将会被传给测试方法的参数列表,这些参数既可以被TestNG注入( java.lang.reflect.Method )或者来源@DataProvider
- 任何@DataProvider可以声明ITestContext类列或者java.lang.reflect.Method类别参数。后者接受即将被调用的方法
也可以使用@NoInject注解来关闭注入
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");
}
}
18.2 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);
}
}
如果需要更加灵活的指定使用哪个模块来初始化测试类,可以指定一个模块工厂(module factory):
@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);
}
模块工厂需要传入一个测试的实例的上下文(a
n instance of the test context)和需要初始化的测试类(
the test class that TestNG needs to instantiate)。我们的createModule方法需要返回知道如何初始化测试类的Guice Module。我们可以用测试上下文(context)找到更多关于环境的信息,如testng.xml中指定的参数等。
You will get even more flexibility and Guice power with
parent-module
and
guice-stage
suite parameters。Guice-stage允许我们选择Stage来创建父类注解器。默认的一个是DEVELOPMENT。其他允许的值是PRODUCTION和TOOL。下面展示了在test.xml怎么样定义一个parent-module:
<suite parent-module="com.example.SuiteParenModule" guice-stage="PRODUCTION">
</suite>
TestNG会为给定的测试集创建唯一一个模型(module).用这个模型可以得到测试指定Guice模型和模型工厂的实例,然后为每个测试类创建子注入器。用这个方法,我们可以在父类模型声明所有通用(common)的绑定,也可以将在父类模型中声明的绑定注入到模型和模型工厂中。如下是该功能展示:
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);
}
}
package com.example;
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类中,TestModule类中也声明了MySession的绑定。在xml文件中的父类模型设置为ParentModule,这回注解到TestModule。之后,在TestClass类中,有两个注解* MyService(来此ParentModule的绑定)和* MySession(来此TestModule绑定)。这个配置保证了这个测试集中所有的用例会运行在相同的回话实例中,每个测试集会创建一个MyContextImpl对象。这让我们可以为测试集所有测试配置通用的环境。