基于Netty的RPC简单框架实现(五):功能测试与性能测试
2015-09-21 20:28
821 查看
1.JUnit依赖
功能测试使用到了JUnit
2.测试前准备
(1).定义接口JUnitTestInterface
定义了用于测试的方法
(2).实现接口JUnitTestInterface
(3).定义自定义类型JUnitTestCustomObject
(4).定义自定义异常JUnitTestCustomException
3.功能测试
(1).JUnitServerTest
正常启动RPC服务器(维持服务器开启状态,后续功能测试和性能测试需要服务器在线)
(2).JUnitFunctionTest
测试结果:
测试全部通过
(3).JUnitMultiThreadSafeTest
为验证RPC客户端是否是线程安全的,进行线程安全测试
测试结果:
测试通过
(4).RpcPerformanceTest
测试结果:
测试环境:酷睿i5-2410m 4G内存 64位Windows7操作系统
完整的代码提交在:https://github.com/maigo-uestc/Maigo-RPC
功能测试使用到了JUnit
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>在pom.xml中添加上方的依赖
2.测试前准备
(1).定义接口JUnitTestInterface
package com.maigo.rpc.test; import java.util.List; public interface JUnitTestInterface { public String methodWithoutArg(); public String methodWithArgs(String arg1, int arg2); public JUnitTestCustomObject methodWithCustomObject(JUnitTestCustomObject customObject); public List<String> methodReturnList(String arg1, String arg2); public void methodThrowException(); public void methodTimeOut(); public void methodReturnVoid(); public String methodDelayOneSecond(); public int methodForMultiThread(int threadId); public String methodForPerformance(); }
定义了用于测试的方法
(2).实现接口JUnitTestInterface
package com.maigo.rpc.test; import java.util.Arrays; import java.util.List; public class JUnitTestInterfaceImpl implements JUnitTestInterface { public String methodWithoutArg() { return "this is return from methodWithoutArg()"; } public String methodWithArgs(String arg1, int arg2) { return arg1 + " = " + arg2; } public JUnitTestCustomObject methodWithCustomObject( JUnitTestCustomObject customObject) { JUnitTestCustomObject object = new JUnitTestCustomObject(customObject.getString() + " after", customObject.getI() + 47); return object; } public List<String> methodReturnList(String arg1, String arg2) { return Arrays.asList(arg1, arg2); } public void methodThrowException() { throw new JUnitTestCustomException(); } public void methodTimeOut() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } public void methodReturnVoid() { return; } public String methodDelayOneSecond() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "I have sleep 1000ms already."; } public int methodForMultiThread(int threadId) { return threadId; } public String methodForPerformance() { return "Maigo"; } }实现了用于测试的方法
(3).定义自定义类型JUnitTestCustomObject
package com.maigo.rpc.test; public class JUnitTestCustomObject { private String string; private int i; public JUnitTestCustomObject(String string, int i) { super(); this.string = string; this.i = i; } public String getString() { return string; } public void setString(String string) { this.string = string; } public int getI() { return i; } public void setI(int i) { this.i = i; } @Override public boolean equals(Object obj) { JUnitTestCustomObject object = null; if(obj instanceof JUnitTestCustomObject) object = (JUnitTestCustomObject)obj; else return false; return (this.string.equals(object.string)) && (this.i == object.i); } }
(4).定义自定义异常JUnitTestCustomException
package com.maigo.rpc.test; public class JUnitTestCustomException extends RuntimeException { private static final long serialVersionUID = 591530421634999576L; public JUnitTestCustomException() { super("CustomException"); } }
3.功能测试
(1).JUnitServerTest
package com.maigo.rpc.test; import static org.junit.Assert.*; import org.junit.Test; import com.maigo.rpc.server.RpcServer; import com.maigo.rpc.server.RpcServerBuilder; public class JUnitServerTest { @Test public void testServerStart() { JUnitTestInterfaceImpl jUnitTestInterfaceImpl = new JUnitTestInterfaceImpl(); RpcServer rpcServer = RpcServerBuilder.create() .serviceInterface(JUnitTestInterface.class) .serviceProvider(jUnitTestInterfaceImpl) .threads(4) .bind(3721) .build(); rpcServer.start(); } public static void main(String[] args) { new JUnitServerTest().testServerStart(); } }测试结果:
正常启动RPC服务器(维持服务器开启状态,后续功能测试和性能测试需要服务器在线)
(2).JUnitFunctionTest
package com.maigo.rpc.test; import static org.junit.Assert.*; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.BeforeClass; import org.junit.Test; import com.maigo.rpc.aop.RpcInvokeHook; import com.maigo.rpc.client.RpcClientAsyncProxy; import com.maigo.rpc.client.RpcClientProxyBuilder; import com.maigo.rpc.exception.RpcMethodNotFoundException; import com.maigo.rpc.exception.RpcTimeoutException; import com.maigo.rpc.future.RpcFuture; import com.maigo.rpc.future.RpcFutureListener; public class JUnitFunctionTest { /** * use for sync-mode test */ public static JUnitTestInterface jUnitTestInterface; /** * use for async-mode test */ public static RpcClientAsyncProxy rpcClientAsyncProxy; private static int integerBeforeHook = 0; private static int integerAfterHook = 0; private CountDownLatch countDownLatch; @BeforeClass public static void init() { RpcInvokeHook hook = new RpcInvokeHook() { public void beforeInvoke(String methodName, Object[] args) { integerBeforeHook++; } public void afterInvoke(String methodName, Object[] args) { integerAfterHook++; } }; jUnitTestInterface = RpcClientProxyBuilder.create(JUnitTestInterface.class) .timeout(2000) .threads(4) .hook(hook) .connect("127.0.0.1", 3721) .build(); rpcClientAsyncProxy = RpcClientProxyBuilder.create(JUnitTestInterface.class) .timeout(2000) .threads(4) .hook(hook) .connect("127.0.0.1", 3721) .buildAsyncProxy(); } @Test public void testMethodWithoutArg() { assertEquals("this is return from methodWithoutArg()", jUnitTestInterface.methodWithoutArg()); } @Test public void testMethodWithArgs() { assertEquals("age = 23", jUnitTestInterface.methodWithArgs("age", 23)); assertEquals("born = 1992", jUnitTestInterface.methodWithArgs("born", 1992)); } @Test public void methodWithCustomObject() { JUnitTestCustomObject beforeCustomObject = new JUnitTestCustomObject("before", 3); JUnitTestCustomObject afterCustomObject = jUnitTestInterface.methodWithCustomObject(beforeCustomObject); assertEquals("before after", afterCustomObject.getString()); assertEquals(50, afterCustomObject.getI()); } @Test public void testMethodReturnList() { List<String> list = jUnitTestInterface.methodReturnList("hello", "world"); assertEquals("hello", list.get(0)); assertEquals("world", list.get(1)); } @Test(expected=JUnitTestCustomException.class) public void testMethodThrowException() { jUnitTestInterface.methodThrowException(); } @Test(expected=RpcTimeoutException.class) public void testMethodTimeOut() { jUnitTestInterface.methodTimeOut(); } @Test public void testMethodReturnVoid() { jUnitTestInterface.methodReturnVoid(); } @Test public void testMethodHook() { integerBeforeHook = 100; integerAfterHook = 500; jUnitTestInterface.methodWithoutArg(); assertEquals(101, integerBeforeHook); assertEquals(501, integerAfterHook); } @Test public void testFutureSuccess() { RpcFuture rpcFuture = rpcClientAsyncProxy.call("methodDelayOneSecond"); assertNotNull(rpcFuture); assertFalse(rpcFuture.isDone()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } assertTrue(rpcFuture.isDone()); try { assertEquals("I have sleep 1000ms already.", rpcFuture.get()); } catch (Throwable e) { e.printStackTrace(); } } @Test public void testFutureError() { RpcFuture rpcFuture = rpcClientAsyncProxy.call("methodThrowException"); assertNotNull(rpcFuture); try { rpcFuture.get(); } catch (Throwable e) { if(e instanceof JUnitTestCustomException) return; fail(e + " was caught when testFutureError."); } fail("failed to catch JUnitTestCustomException."); } @Test public void testFutureListener() { //1.test for get a result by RpcFutureListener countDownLatch = new CountDownLatch(1); RpcFuture rpcFuture = rpcClientAsyncProxy.call("methodDelayOneSecond"); assertNotNull(rpcFuture); rpcFuture.setRpcFutureListener(new RpcFutureListener() { public void onResult(Object result) { countDownLatch.countDown(); } public void onException(Throwable throwable) { fail(throwable + " was caught when testFutureListener."); } }); try { if(!countDownLatch.await(2000, TimeUnit.MILLISECONDS)) fail("failed to get result by RpcFutureListener."); } catch (InterruptedException e) { fail(e + " was caught when testFutureListener."); } //2.test for get a exception by RpcFutureListener countDownLatch = new CountDownLatch(1); rpcFuture = rpcClientAsyncProxy.call("methodThrowException"); assertNotNull(rpcFuture); rpcFuture.setRpcFutureListener(new RpcFutureListener() { public void onResult(Object result) { fail("failed to catch JUnitTestCustomException."); } public void onException(Throwable throwable) { if(throwable instanceof JUnitTestCustomException) countDownLatch.countDown(); else fail("failed to catch JUnitTestCustomException."); } }); try { if(!countDownLatch.await(1000, TimeUnit.MILLISECONDS)) fail("failed to get exception by RpcFutureListener."); } catch (InterruptedException e) { fail(e + " was caught when testFutureListener."); } } @Test(expected=RpcMethodNotFoundException.class) public void testMethodNotFound() { rpcClientAsyncProxy.call("methodWhichIsNotExist"); } }代码量较大,但都简单易懂,对每个方法调用后加上断言判断结果是否符合预期。其中包含了对同步方式和异步方式的测试,异步方式中对RpcFuture和RpcFutureListener也进行了测试。测试的内容包括0个或多个参数,使用自定义类型的参数,返回自定义类型的参数,返回List<T>,方法抛出异常,方法调用超时,方法返回空类型,设置的钩子函数Hook的调用等。由于同步方式直接调用实现了接口的代理对象的方法,不存在找不到方法的情况。而异步方式下使用call()方法进行调用,有可能存在找不到与参数1同名的方法的情况,此时会抛出RpcMethodNotFoundException,因此异步方式下还应对此情景进行测试。因此此功能测试基本涵盖本框架使用的方方面面。
测试结果:
测试全部通过
(3).JUnitMultiThreadSafeTest
为验证RPC客户端是否是线程安全的,进行线程安全测试
package com.maigo.rpc.test; import static org.junit.Assert.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.BeforeClass; import org.junit.Test; import com.maigo.rpc.client.RpcClientProxyBuilder; public class JUnitMultiThreadSafeTest { public static JUnitTestInterface jUnitTestInterface; public final static int THREADS = 20; public final static int INVOKES = 1000; public final static int TIMEOUT = 10000; @BeforeClass public static void init() { jUnitTestInterface = RpcClientProxyBuilder.create(JUnitTestInterface.class) .timeout(2000) .threads(4) .connect("127.0.0.1", 3721) .build(); } @Test public void testMultiThreadSafe() { ExecutorService threadPool = Executors.newFixedThreadPool(THREADS); CountDownLatch countDownLatch = new CountDownLatch(THREADS * INVOKES); for(int i=0; i<THREADS; i++) { threadPool.execute(new JUnitMultiThreadSafeTestThread(i, jUnitTestInterface, countDownLatch)); } try { if(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) return; else fail("some thread did not finish all the invoke."); } catch (InterruptedException e) { e.printStackTrace(); } } private static class JUnitMultiThreadSafeTestThread extends Thread { private int threadId; private JUnitTestInterface jUnitTestInterface; private CountDownLatch countDownLatch; public JUnitMultiThreadSafeTestThread(int threadId, JUnitTestInterface jUnitTestInterface, CountDownLatch countDownLatch) { this.threadId = threadId; this.jUnitTestInterface = jUnitTestInterface; this.countDownLatch = countDownLatch; } @Override public void run() { for(int i=0; i<INVOKES; i++) { int result = jUnitTestInterface.methodForMultiThread(threadId); if(result == threadId) countDownLatch.countDown(); } } } }开启了20个线程使用共享的RpcClient代理对象jUnitTestInterface各进行1000次方法调用。每个线程拥有唯一的线程Id,所调用的方法会直接返回其参数,每个线程中对methodForMultiThread()方法的调用送进本线程的Id,当且仅当methodForMultiThread()方法的返回值等于本线程Id才会让countDownLatch减1。每个线程只会进行1000次调用,故若由于线程不安全导致某次调用失败或调用结果被错误的线程获取到,countDownLatch就不可能递减至0了,此时测试将不通过。而若RPC客户端是线程安全的,则countDownLatch能顺利递减至0,测试通过。
测试结果:
测试通过
(4).RpcPerformanceTest
package com.maigo.rpc.test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import com.maigo.rpc.client.RpcClientProxyBuilder; import com.maigo.rpc.utils.InfoPrinter; public class RpcPerformanceTest { public static JUnitTestInterface jUnitTestInterface; public final static int THREADS = 16; public final static int INVOKES = 10000; public final static int TIMEOUT = 300; public static void main(String[] args) throws Exception { jUnitTestInterface = RpcClientProxyBuilder.create(JUnitTestInterface.class) .timeout(0) .threads(4) .connect("127.0.0.1", 3721) .build(); ExecutorService threadPool = Executors.newFixedThreadPool(THREADS); CountDownLatch countDownLatch = new CountDownLatch(THREADS * INVOKES); InfoPrinter.println("RpcPerformanceTest started."); long startTime = System.currentTimeMillis(); for(int i=0; i<THREADS; i++) { threadPool.execute(new RpcPerformanceTestThread(jUnitTestInterface, countDownLatch)); } if(countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { long endTime = System.currentTimeMillis(); double tps = THREADS * INVOKES / ((endTime - startTime) / 1000f); InfoPrinter.println("RpcPerformanceTest finished."); InfoPrinter.println("Result tps = " + tps); } else { InfoPrinter.println("RpcPerformanceTest failed."); } } private static class RpcPerformanceTestThread extends Thread { private JUnitTestInterface jUnitTestInterface; private CountDownLatch countDownLatch; public RpcPerformanceTestThread(JUnitTestInterface jUnitTestInterface, CountDownLatch countDownLatch) { this.jUnitTestInterface = jUnitTestInterface; this.countDownLatch = countDownLatch; } @Override public void run() { for(int i=0; i<INVOKES; i++) { jUnitTestInterface.methodForPerformance(); countDownLatch.countDown(); } } } }开启了16个线程,每个线程进行10000次方法调用。调用结束用调用总数160000除以耗费的时间即可得到每秒处理的调用请求数。
测试结果:
测试环境:酷睿i5-2410m 4G内存 64位Windows7操作系统
[2015-09-21 23:40:05]:Try to connect to [127.0.0.1:3721]. [2015-09-21 23:40:05]:Connect to [127.0.0.1:3721] successed. [2015-09-21 23:40:05]:RpcPerformanceTest started. [2015-09-21 23:40:14]:RpcPerformanceTest finished. [2015-09-21 23:40:14]:Result tps = 16872.298828125
完整的代码提交在:https://github.com/maigo-uestc/Maigo-RPC
相关文章推荐
- ACM-ICPC国际大学生程序设计竞赛北京赛区(2015)网络赛 B Mission Impossible 6
- office 2003 完全 卸载 工具 来自微软官方
- office 2003 完全 卸载 工具 来自微软官方
- 数据库之多表查询
- c/c++面试1——字符串替换程序
- 牛客堂常见面试题精讲(一)3
- nefu559分书问题【类8皇后】
- Redis和Memcache的区别分析
- bzoj1673:天平
- [UI基础]day01
- Java8揭秘(四)Java集合类库的批量数据操作
- UC/OS II 软件定时器
- 1008. 数组元素循环右移问题 (20)
- 嵌入式软件开发——嵌入式软件工程师笔试题
- 我们神码小组第一次合作完成的java项目——类的多态继承与接口的使用练习。
- Win8系统下Oracle 11g release 2 安装流程记录
- 旋转六面体源码
- 0917 实验一 词法分析程序
- 软链接/硬链接
- Linux环境变量的设置和查看方法 http://soft.chinabyte.com/os/169/11412169.shtml