想像一下,我去测试一个服务器。为了完成测试这个服务器,我的测试类class包含以下的test method:
- 检查下我们是否在正确的jvm上运行
- 检查下服务器是否成功启动
- 大约20种访问服务器的method, 例如 登录请求,支付请求
显然,上述选项的前两个应该在所有事情之前运行。如果JUnit实现这样,需要前两个方法放在初始化的代码中,可能还需要是静态static
的(只被初始实例化一次),因为JUnit实例化一个新的对象在每个test method
触发,我们也无法使用setup方法因为在每个test method前都要被触发。初始化的代码将设置两个boolean值,20种test method 需要在执行前测试这两个boolean值,如果他们之一fail
了,test method
就fail
了。
现在假设在一个测试案例种,我使用了错误的jvm。这种情况下,JUint 将会报告这个错误在初始化的代码种,然后其余的20个test method
也都fail
。
这种情况下是非常bad
的:
- 初始化的代码并不是
test method
中,他的成功/失败 应该分开测试的,不应该跟20个混在一起 - 当QA读取测试的结果,他们将会看到,成功1失败20,QA心里会骂娘吧
- 一旦读到测试结果,QA将会准备修20个
tests
,实际只有一个。
这就是为什么我坚信,一个test framwork需要提供对 dependent test methods的支持,可以定义methd b()
为依赖于method a()
的run成功(也就是b的执行,依赖与a执行的成功)。如果a()
失败了,b()
将会被标记skip 而不是fail。
有了这个特性,测试的结果就成为成功1个失败一个skip20个,这种结果更加能反映真实的情况。
在Testng中,代码如下组织:
@Test
public correctVM() {}
@Test
public serverStartedOk() {}
@Test(dependsOnMethods = { "correctVM", "serverStartedOk" })
public method1() {}
...
这是一个好的开始,但是需要列出所有的被依赖的方法,😀 instead,我们使用testng的group
@Test(groups = { "init" })
public correctVM() {}
@Test(groups = { "init" })
public serverStartedOk() {}
@Test(dependsOnGroups = { "init.* })
public method1() {}
...
从这个group的设置,我们获得了很多的灵活性。举个例子,想象我想添加一个init test method
,比如,打开防火墙。我所需要做的就是添加这个方法并且宣称他是group init
的一部分。
另外,也要注意的是,在依赖的group
中,我使用正则表达式。这暗示,你可以定义多个Init group
(例如 initOS
initJVM
等),在test method
被触发之前,这些group
会被自动的run
。
然而,我们依然可以做的更好。
在上面的例子中,我不喜欢这样的事实:无论何时只要我添加一个real test method
,我需要记得去指定它依赖的group init.*
(例如,几个月后,你想给之前的项目添加一些新的测试案例,这时候,你很可能已经忘记类似的想法需要哪些依赖了)。在testng
中,老道的做法是添加注解,使得在test class
这个层面上都不用添加依赖。
同样,我也不喜欢Init
方法与real test method
在同一个类中,因此我使用继承来提供一个干净的规则拆分。
因此,我将重新组织我的tests
,像这样:
@Test(groups = "init")
public class BaseTest {
public correctVM() {}
public serverStartedOk() {}
}
这是对我tests
的基类。 @Test
注解注释这个类,表示使用这个类种所有的public method。因此,每个测试方法自动的成为group init的一部分了。
接着,将我的tests 写成BaseTest
的一个子类:
@Test(dependsOnGroups = { "init.*" })
public class TestServer extends BaseTest {
public method1() { ... }
public method2() { ... }
...
}
这里,又一次,@Test
注解,意味着,表示使用这个类种所有的public method
。又因为这个类继承自BaseTest
,他不仅能接触到继承的方法,还有注解,因此Testng将会包括所有 属于group init.*
的测试方法。
总结
- 这两个类的职责划分得非常清楚。
- 很容易维护和升级,因为添加了测试方法(无论
它们是init或“真实的”测试方法)归结起来就是添加这些
方法到正确的类(不需要注释它们)。新来的人
甚至不需要知道需要什么注释才能得到所有 - 准确反映测试运行结果
将正确地从
级联效应,因此其解决方案应推迟到
失败已经解决了
这很管用。
原文连接:Testng