这一章太多了,分两次
第三章 实例
这一章主要包括JRUNIT项目中的一些例子,这些例子主要是关于C/S架构的测试。基于一组相同的、基本的客户端和服务器的代码,利用socket从客户端发送一个字符串到服务器,服务器再返回一个固定的字符串给客户端,这一章所有的例子都会籍此代码来展示microbenchmark和jrunit的不同。(All the samples illustrate the different features of microbenchmark and jrunit using the same base client and server base code which uses a socket to send a String to the server from the client, which is then returned a static String from the server. )所有的实例均可在IDE中编译和运行,有些还可以通过JRUNIT根目录下的ant构建脚本来运行。之所以只有部分才能在ant脚本中,是因为他们可以被完全自动化(接下来将会解释这一点)。还有一点需要注意的是,下面每个实例所在章节的标题,都与org.jboss.jrunit.sample这个包相关,这些例子都可以在这个包所在路径下找到。
basic
第一个C/S测试的例子在org.jboss.jrunit.sample.basic包中,包括SimpleServerTest和SimpleServerTest。后面介绍各个不同特色的test case都是基于这两个test的。SimpleClientTest是一个基本的JUNIT test case,包含两个test方法,testClientCall()和testFailure{}方法
//代码
public class SimpleClientTest extends TestCase
{
private String address = "localhost";
private int port = 6700;
private Socket socket = null;
private ObjectOutputStream oos;
private ObjectInputStream objInputStream;
public void testClientCall() throws Exception
{
getSocket();
oos.writeObject("This is the request from " + Thread.currentThread().getName());
oos.reset();
oos.writeObject(Boolean.TRUE);
oos.flush();
oos.reset();
Object obj = objInputStream.readObject();
objInputStream.readObject();
assertEquals("This is response.", obj);
}
public void testFailure()
{
fail("This is a check to make sure failures reported.");
}
private void getSocket() throws IOException
{
if(socket == null)
{
try
{
socket = new Socket(address, port);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
oos = new ObjectOutputStream(out);
objInputStream = new ObjectInputStream(in);
}
catch(IOException e)
{
e.printStackTrace();
}
}
else
{
oos.reset();
oos.writeByte(1);
oos.flush();
oos.reset();
objInputStream.readByte();
}
}
}
testClientCass方法会打开一个socket连接到服务器,发送一条字符串过去,然后检查从服务器返回的字符串,看看它的值是不是和预期的一样(通过JUNIT的assert),而testFailure方法只是使用一个junit的fail assertion
SimpleServerTest也是一个基本的JUNIT test case,在它的setUp方法里(一个独立的线程内)建立了一个socket服务器,接收来自客户端的请求并返回固定的字符串。SimpleServerTest也有一个test方法,叫做testRequestCount,这个方法每三秒打印出接受到的请求数目
//代码
public class SimpleServerTest extends TestCase //ServerTestCase
{
private int serverBindPort = 6700;
private int backlog = 200;
private ServerSocket serverSocket;
private InetAddress bindAddress;
private String serverBindAddress = "localhost";
private int timeout = 5000; // 5 seconds.
private boolean continueToRun = true; // flag to keep the server listening
private int requestCounter = 0;
protected void setUp() throws Exception
{
System.out.println("SimpleServerTest::setUp() called.");
bindAddress = InetAddress.getByName(serverBindAddress);
serverSocket = new ServerSocket(serverBindPort, backlog, bindAddress);
// this was done inline since TestCase already has a void parameter run() method
// so could not create a run() method for the Runnable implementation.
new Thread()
{
public void run()
{
try
{
startServer();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}.start();
}
public void testRequestCount()
{
while(continueToRun)
{
try
{
Thread.currentThread().sleep(3000);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("Requests taken: " + requestCounter);
}
assertEquals(30, requestCounter);
}
protected void tearDown() throws Exception
{
continueToRun = false;
System.out.println("Tearing down. Processed " + requestCounter + " requests"
if(serverSocket != null && !serverSocket.isClosed())
{
serverSocket.close();
serverSocket = null;
}
}
private void startServer()
{
while(continueToRun)
{
try
{
Socket socket = serverSocket.accept();
socket.setSoTimeout(timeout);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStr
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.flush();
ObjectInputStream objInputStream = new ObjectInputStream(bis);
processRequest(objInputStream, oos);
while(continueToRun)
{
acknowledge(objInputStream, oos);
processRequest(objInputStream, oos);
}
}
catch(IOException e)
{
System.out.println("Done processing on client socket.");
}
catch(ClassNotFoundException e)
{
}
}
}
...
这段代码反映了以C/S模式进行junit测试的一个问题。客户端在它运行整个测试期间需要一个服务器来接收它的请求。服务器则按照junit的传统模式,先set up,然后test,最后tear down。当服务器和客户端都跑起来时,SimpleClientTest的testClientCall将会测试通过,但是SimpleServerTest的testRequestCount却永远不会结束,tearDown也将没法被调用
如果想停止SimpleServerTest,它所在的线程就必须被杀死。如果改变SimpleServerTest的testRequestCount方法,使得它不再循环,那运行SimpleServerTest的测试就可能在SimpleClientTest有机会调用它之前就结束了,导致testClientCall失败
decorated
虽然这个实例不会直接用到jrunit的特性,但它却显示了使用基准修饰符(benchmark decorator)的好处。这个实例对刚才的SimpleClientTest和SimpleServerTest作了少许修改。
//代码
public static Test suite()
{
return new ThreadLocalDecorator(SimpleClientTest.class, 3, 10, 0, true);
}
public void testClientCall() throws Exception
{
// start benchmark tracking
ThreadLocalBenchmark.openBench("ClientCall");
ThreadLocalBenchmark.openBench("GetSocket");
getSocket();
ThreadLocalBenchmark.closeBench("GetSocket");
/**
* Uncomment this to see that is the same object, thread, and socket for each loop,
* but will be different for each thread as specified to the test decorator. This
* is based on the values passed to JunitThreadDecorator (or one of it's subclasses)
* for the numberOfThreads and loop parameter values.
*/
/**
* 把下面的注释去掉,就可以看出每次循环都是相同的对象、线程和socket,但不同线程之间是不同的,
* 这跟传给JunitThreadDecorator(或者它的子类)构造器参数:numberOfThreads和loop的值有关
*/
//System.out.println(hashCode() + " - " + Thread.currentThread().getName() + " -" + socket.hashCod
oos.writeObject("This is the request from " + Thread.currentThread().getName());
oos.reset();
oos.writeObject(Boolean.TRUE);
oos.flush();
oos.reset();
Object obj = objInputStream.readObject();
objInputStream.readObject();
assertEquals("This is response.", obj);
ThreadLocalBenchmark.closeBench("ClientCall");
}
修改了SimpleClientTest后,增加了一个suite方法,这个方法将返回一个org.jboss.jrunit.decorators.ThreadLocalDecorator类的新实例。在本例中,传递给decorator的参数使它在运行org.jboss.jrunit.sample.decorated.SimpleClientTest这个test case时,使用三个线程数,每个线程十次循环,没有延时(你可以参考ThreadLocalDecorator这一节来获得更多关于ThreadLocalDecorator的信息)
这意味着当SimpleClientTest运行时,会有三个(线程)实例被创建,每个线程都会运行所有的test方法十次。SimpleClientTest类的testClientCall方法也加了几行代码,用于标记(mark)某个benchmark的开始和结束。在这里,ClientCall和GotSocket这两个benchmark在方法的开头处开始记录
GotSocket基准在进行完getSocket方法的全部调用后中止,而ClientCall基准在方法末尾中止,目前SimpleServerTest并没有被修改,因此服务器代码一次只接受一个连接,处理完请求后,连接关闭,然后再处理下一个连接。
要运行这个实例,首先启动org.jboss.jrunit.sample.basic.decorated.SimpleServerTest,然后启动org.jboss.jrunit.sample.decorated.SimpleClientTest 。客户端运行完毕后,将服务器停止,在客户端控制台底部会有类似这样的一行:
SubBenchmarks: Benchmark:ClientCall Executions:30 Time:15311 SubBenchmarks: Benchmark:GetSocket Executions:30 Time:15191
这表明三个线程分别对testClientCall和getSocket方法运行十次花了多少时间。现在,把SimpleServerTest类的setUp方法中循环体内的注释去掉(译注:在SimpleServerTest中根本找不到所谓的循环体,从下文看估计是在new Thread外面有一个循环,用于生成多个thread),那样就会有三个可用的线程来接收客户端发来的请求了(请千万不要向我抱怨说服务器不该这样编码的....别太认真,这只是演示而已:))。编译并再次运行,你应该会得到像这样的结果:
SubBenchmarks: Benchmark:ClientCall Executions:30 Time:130 SubBenchmarks: Benchmark:GetSocket Executions:30 Time:30
这个结果表明,把服务器改成多线程后可以并发地接收和处理所有客户端的请求,因此客户端的方法调用比原来快乐100多倍。
第三章 实例
这一章主要包括JRUNIT项目中的一些例子,这些例子主要是关于C/S架构的测试。基于一组相同的、基本的客户端和服务器的代码,利用socket从客户端发送一个字符串到服务器,服务器再返回一个固定的字符串给客户端,这一章所有的例子都会籍此代码来展示microbenchmark和jrunit的不同。(All the samples illustrate the different features of microbenchmark and jrunit using the same base client and server base code which uses a socket to send a String to the server from the client, which is then returned a static String from the server. )所有的实例均可在IDE中编译和运行,有些还可以通过JRUNIT根目录下的ant构建脚本来运行。之所以只有部分才能在ant脚本中,是因为他们可以被完全自动化(接下来将会解释这一点)。还有一点需要注意的是,下面每个实例所在章节的标题,都与org.jboss.jrunit.sample这个包相关,这些例子都可以在这个包所在路径下找到。
basic
第一个C/S测试的例子在org.jboss.jrunit.sample.basic包中,包括SimpleServerTest和SimpleServerTest。后面介绍各个不同特色的test case都是基于这两个test的。SimpleClientTest是一个基本的JUNIT test case,包含两个test方法,testClientCall()和testFailure{}方法
//代码
public class SimpleClientTest extends TestCase
{
private String address = "localhost";
private int port = 6700;
private Socket socket = null;
private ObjectOutputStream oos;
private ObjectInputStream objInputStream;
public void testClientCall() throws Exception
{
getSocket();
oos.writeObject("This is the request from " + Thread.currentThread().getName());
oos.reset();
oos.writeObject(Boolean.TRUE);
oos.flush();
oos.reset();
Object obj = objInputStream.readObject();
objInputStream.readObject();
assertEquals("This is response.", obj);
}
public void testFailure()
{
fail("This is a check to make sure failures reported.");
}
private void getSocket() throws IOException
{
if(socket == null)
{
try
{
socket = new Socket(address, port);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
oos = new ObjectOutputStream(out);
objInputStream = new ObjectInputStream(in);
}
catch(IOException e)
{
e.printStackTrace();
}
}
else
{
oos.reset();
oos.writeByte(1);
oos.flush();
oos.reset();
objInputStream.readByte();
}
}
}
testClientCass方法会打开一个socket连接到服务器,发送一条字符串过去,然后检查从服务器返回的字符串,看看它的值是不是和预期的一样(通过JUNIT的assert),而testFailure方法只是使用一个junit的fail assertion
SimpleServerTest也是一个基本的JUNIT test case,在它的setUp方法里(一个独立的线程内)建立了一个socket服务器,接收来自客户端的请求并返回固定的字符串。SimpleServerTest也有一个test方法,叫做testRequestCount,这个方法每三秒打印出接受到的请求数目
//代码
public class SimpleServerTest extends TestCase //ServerTestCase
{
private int serverBindPort = 6700;
private int backlog = 200;
private ServerSocket serverSocket;
private InetAddress bindAddress;
private String serverBindAddress = "localhost";
private int timeout = 5000; // 5 seconds.
private boolean continueToRun = true; // flag to keep the server listening
private int requestCounter = 0;
protected void setUp() throws Exception
{
System.out.println("SimpleServerTest::setUp() called.");
bindAddress = InetAddress.getByName(serverBindAddress);
serverSocket = new ServerSocket(serverBindPort, backlog, bindAddress);
// this was done inline since TestCase already has a void parameter run() method
// so could not create a run() method for the Runnable implementation.
new Thread()
{
public void run()
{
try
{
startServer();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}.start();
}
public void testRequestCount()
{
while(continueToRun)
{
try
{
Thread.currentThread().sleep(3000);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println("Requests taken: " + requestCounter);
}
assertEquals(30, requestCounter);
}
protected void tearDown() throws Exception
{
continueToRun = false;
System.out.println("Tearing down. Processed " + requestCounter + " requests"
if(serverSocket != null && !serverSocket.isClosed())
{
serverSocket.close();
serverSocket = null;
}
}
private void startServer()
{
while(continueToRun)
{
try
{
Socket socket = serverSocket.accept();
socket.setSoTimeout(timeout);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStr
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.flush();
ObjectInputStream objInputStream = new ObjectInputStream(bis);
processRequest(objInputStream, oos);
while(continueToRun)
{
acknowledge(objInputStream, oos);
processRequest(objInputStream, oos);
}
}
catch(IOException e)
{
System.out.println("Done processing on client socket.");
}
catch(ClassNotFoundException e)
{
}
}
}
...
这段代码反映了以C/S模式进行junit测试的一个问题。客户端在它运行整个测试期间需要一个服务器来接收它的请求。服务器则按照junit的传统模式,先set up,然后test,最后tear down。当服务器和客户端都跑起来时,SimpleClientTest的testClientCall将会测试通过,但是SimpleServerTest的testRequestCount却永远不会结束,tearDown也将没法被调用
如果想停止SimpleServerTest,它所在的线程就必须被杀死。如果改变SimpleServerTest的testRequestCount方法,使得它不再循环,那运行SimpleServerTest的测试就可能在SimpleClientTest有机会调用它之前就结束了,导致testClientCall失败
decorated
虽然这个实例不会直接用到jrunit的特性,但它却显示了使用基准修饰符(benchmark decorator)的好处。这个实例对刚才的SimpleClientTest和SimpleServerTest作了少许修改。
//代码
public static Test suite()
{
return new ThreadLocalDecorator(SimpleClientTest.class, 3, 10, 0, true);
}
public void testClientCall() throws Exception
{
// start benchmark tracking
ThreadLocalBenchmark.openBench("ClientCall");
ThreadLocalBenchmark.openBench("GetSocket");
getSocket();
ThreadLocalBenchmark.closeBench("GetSocket");
/**
* Uncomment this to see that is the same object, thread, and socket for each loop,
* but will be different for each thread as specified to the test decorator. This
* is based on the values passed to JunitThreadDecorator (or one of it's subclasses)
* for the numberOfThreads and loop parameter values.
*/
/**
* 把下面的注释去掉,就可以看出每次循环都是相同的对象、线程和socket,但不同线程之间是不同的,
* 这跟传给JunitThreadDecorator(或者它的子类)构造器参数:numberOfThreads和loop的值有关
*/
//System.out.println(hashCode() + " - " + Thread.currentThread().getName() + " -" + socket.hashCod
oos.writeObject("This is the request from " + Thread.currentThread().getName());
oos.reset();
oos.writeObject(Boolean.TRUE);
oos.flush();
oos.reset();
Object obj = objInputStream.readObject();
objInputStream.readObject();
assertEquals("This is response.", obj);
ThreadLocalBenchmark.closeBench("ClientCall");
}
修改了SimpleClientTest后,增加了一个suite方法,这个方法将返回一个org.jboss.jrunit.decorators.ThreadLocalDecorator类的新实例。在本例中,传递给decorator的参数使它在运行org.jboss.jrunit.sample.decorated.SimpleClientTest这个test case时,使用三个线程数,每个线程十次循环,没有延时(你可以参考ThreadLocalDecorator这一节来获得更多关于ThreadLocalDecorator的信息)
这意味着当SimpleClientTest运行时,会有三个(线程)实例被创建,每个线程都会运行所有的test方法十次。SimpleClientTest类的testClientCall方法也加了几行代码,用于标记(mark)某个benchmark的开始和结束。在这里,ClientCall和GotSocket这两个benchmark在方法的开头处开始记录
GotSocket基准在进行完getSocket方法的全部调用后中止,而ClientCall基准在方法末尾中止,目前SimpleServerTest并没有被修改,因此服务器代码一次只接受一个连接,处理完请求后,连接关闭,然后再处理下一个连接。
要运行这个实例,首先启动org.jboss.jrunit.sample.basic.decorated.SimpleServerTest,然后启动org.jboss.jrunit.sample.decorated.SimpleClientTest 。客户端运行完毕后,将服务器停止,在客户端控制台底部会有类似这样的一行:
SubBenchmarks: Benchmark:ClientCall Executions:30 Time:15311 SubBenchmarks: Benchmark:GetSocket Executions:30 Time:15191
这表明三个线程分别对testClientCall和getSocket方法运行十次花了多少时间。现在,把SimpleServerTest类的setUp方法中循环体内的注释去掉(译注:在SimpleServerTest中根本找不到所谓的循环体,从下文看估计是在new Thread外面有一个循环,用于生成多个thread),那样就会有三个可用的线程来接收客户端发来的请求了(请千万不要向我抱怨说服务器不该这样编码的....别太认真,这只是演示而已:))。编译并再次运行,你应该会得到像这样的结果:
SubBenchmarks: Benchmark:ClientCall Executions:30 Time:130 SubBenchmarks: Benchmark:GetSocket Executions:30 Time:30
这个结果表明,把服务器改成多线程后可以并发地接收和处理所有客户端的请求,因此客户端的方法调用比原来快乐100多倍。