尝试翻译JRUnit的文档(三)

     这一章太多了,分两次
    第三章 实例
    这一章主要包括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多倍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值