C/S测试
首先要解决的是服务器端测试的生命周期问题,因为它需要从junit生命周期管理的默认行为中分离出来(单独讨论)
首先需要有一种方法来通知服务器端启动和初始化,可以用setup方法做到。一旦这个方法返回,也就可以假设服务器已经作好接收调用的准备了。其次,需要告之服务器进行关闭和清理回收资源,可以用tearDown方法来实现,只有在所有客户端都已经完成对服务器端的调用时,才能使用tearDown方法
通过继承org.jboss.jrunit.ServerTestCase类而不是以往的junit.framework.TestCase类,可以实现上述行为。这个ServerTestCase类实际上继承自TestCase类,不过重写了其中一些方法来实现所需功能
正如在junit测试过程中一样,server的实现还需要有一个test方法,用于在setUp后调用。test方法中含有assert(断言),用于验证服务器端数据和metrics(度量?)
这里有几个重点....服务器的test case只能有一个test方法,这是因为junit每运行一个test方法,就会新建一个测试实例来专门运行这个test方法,因此如果有多个test方法,就会有多个服务器test case的实例。如果非要改变这一机制,就得对junit本身进行相当大的改动,所以请你只使用一个test方法(或者你也可以自己去修改junit)
还有很重要的一点是,tearDown方法可能会在某个方法还在运行期间就被调用了。你可以故意这么做,让一个test方法一直循环直到tearDown被调用为止。举个例子,可以像这样:
...
public void testServerMetrics()
{
while(!stop)
{
// collect data here
}
}
protected void tearDown()
{
stop = true;
// so will cause testServerMetrics() to break out of loop
// do shutdown and clean up code.
}
...
public void testServerMetrics()
{
while(!stop)
{
// collect data here
}
}
protected void tearDown()
{
stop = true;
// so will cause testServerMetrics() to break out of loop
// do shutdown and clean up code.
}
...
对于客户端test case来说,jrunit没有什么特别的要求,只要继承了junit.framework.TestCase类,确保满足了junit test case的要求就可以了。(译注:就是和以前用junit一样)
最后,还有个org.jboss.jrunit.TestDriver类,用于表示客户端和服务器端测试的“驱动”。这个类可以产生新的test harness,把客户端和服务器端的test case放在里面跑。这个测试驱动可以通过一个JGroup消息总线与那些test harness进行通讯,从而控制服务器和所有客户端的test case,并从它们那里获得测试结果
逻辑上,运行一个由test runner控制的测试的顺序如下:
(The logical order of a test run as controlled by the test runner is: )
(译注:这个test runner可能是指刚才所说的driver)
1.为每个客户端和服务器端test case生成一个新的test harness
2.等待确认,直至所以test harness创建完毕,它们各自的消息总线也已经启动
3.一旦收到确认,会等待服务器端的test case启动(比如,调用服务器test case的setUp方法)。否则,假如没有收到确认,杀死所以进程,返回出错信息给JUnit。(译注:可见jrunit只是junit的一个扩展,底层还是依赖于junit的实现,比如错误提示)
4.确认服务器端启动完毕后,通知所有的test case(客户端和服务器的)开始运行。否则(如果没有确认信息),对所有test harness发送退出信息。
5.等待客户端test case的结果
6.一旦收到全部客户端返回的结果,就通知服务器进行tear down(例如,调用服务器test case的teardown方法)。否则,杀死所以进程返回出错信息给JUnit
7.等待服务器端的测试结果(当然服务器test case要有test方法)
8.处理所有测试结果,提交给JUnit TestResult类,测试结果会以普通Junit测试报告的方式呈现出来。
9.等待服务器关闭的信息,表明服务器已经成功地关闭并清理完资源
10.关闭消息总线和根测试(root test run),返回至Junit的执行线程中。
对于test driver,用户唯一需要进行编码的地方就是实现一个继承自org.jboss.jrunit.TestDriver的抽象类,并实现其中的declareTestClasses()方法。在这个方法内部调用TestDriver类的addTestClasses方法,并且指定客户端test case类、客户端的并发数量以及服务器端test case类
基准修饰符(Benchmark Decorator)
JRunit使用基准修饰符来提供除了普通Junit测试结果外的一些数据,称为基准结果
org.jboss.jrunit.decorators.ThreadLocalDecorator(以及其他可以接收线程个数和循环次数(等参数)的修饰符)——指定多少个线程数,就会有多少个执行同一个测试的线程被创建。指定多少次循环,就会在每个测试实例中,对每个测试方法循环那么多次。比如,如果指定3个线程、10次循环,那么将会有三个线程执行该测试用例(也即创建了三个测试实例),每个测试实例中,会将里面的所以test方法执行10遍。下面是一个相关的例子:
public class SimpleThreadLoopCounter extends TestCase
{
private static int staticCounter = 0;
private static int staticMethodCounter = 0;
private int localCounter = 0;
public static Test suite()
{
return new ThreadLocalDecorator(SimpleThreadLoopCounter.class, 3, 10, 0, true, true);
}
public SimpleThreadLoopCounter()
{
staticCounter++;
}
public void testCounter() throws Exception
{
System.out.println("staticCounter = " + staticCounter);
System.out.println("staticMethodcounter = " + ++staticMethodCounter);
System.out.println("localCounter = " + ++localCounter);
}
}
运行这个例子,最后的输出会是:
staticCounter = 3 staticMethodcounter = 30 localCounter = 10
有个问题是关于junit如何处理多个test方法(译注:具体方式在开头已经有所说明)。对于每个test case中的每个test方法,junit都会创建一个新的测试实例来运行这个方法。为了说明这一点,我们复制上面代码的testCounter方法(把复制后的改名为testCounter2),那么在输出结果中最后一行变为:
staticCounter = 6 staticMethodcounter = 60 localCounter = 10
(译注:指定了三个线程,junit又为每个测试类创建两个实例,所以总共3×2个,但是对于每一个,它的localCounter 还是10,即执行了10次循环)
JRUnit和修饰符的注意事项:如果numberOfThreads大于1,那ServerTestHarness不会运行。但是numberOfThreads可以拿来模拟同一时刻并发的客户端数量,从这个意义上来说,我认为这不算是个大问题。如果要用ServerTestHarness来这么做,事实上也的确可以产生多个客户端。即时这些客户端不是在一个进程中并发运行,起码也是在各个单独的进程中并发。而能够设定循环变量也是很有必要的,虽然这样会让客户端额外地调用服务器一段时间(或者反复调用)。因此,如果进行远程测试并且要用ThreadLocalDecorator获取基准数据的话,你可以使用下面这个ThreadLocalDecorator的构造器,它的线程数是默认为1的
public ThreadLocalDecorator(Class testClazz, int loops)
延伸阅读
文章来源于领测软件测试网 https://www.ltesting.net/