学习TDD(2)--实例:ProtoStuffUtil类的测试
上篇讲了TDD的步骤和各种好处,俗话说的好,站在岸上是学不会游泳的。所以我们还是要拿个例子出来,实践一下TDD。 因为是第一次尝试,我想还是选个简单的例子,之前写的那个ProtoStuffUtil类就很不错。这个类主要负责对象跟byte[]之间的相互转换。可以参考http://www.52php.cn/article/p-mhbggrwc-pm.html。其实这个类已经写好了,不太符合TDD的规范。但是体验一下还是可以的。 配套的代码已经上传到http://download.csdn.net/detail/mrbcy/9748501 这个类虽然简单但是测试的流程还是很曲折 测试目标因为这个类是负责对象跟byte[]之间的相互转换,所以我想从两个方面测试它:
测试代码来看对象类代码 package tech.mrbcy.mrpc.test.domain; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import com.sun.org.apache.bcel.internal.generic.NEW; import tech.mrbcy.mrpc.test.enumm.UserType; public class User { private int userId; private String userName; private boolean lockState; private UserType userType; private List<String> addresses = new ArrayList<String>(); private Map<String,String> favoriteMap = new HashMap<String,String>(); // getters and setters public void addAddress(String address){ addresses.add(address); } public void putFavor(String key,String value){ favoriteMap.put(key,value); } @Override public String toString() { return "User [userId=" + userId + ",userName=" + userName + ",lockState=" + lockState + ",userType=" + userType + ",addresses=" + addresses + ",favoriteMap=" + favoriteMap + "]"; } } 看起来对象很复杂了,各种的数据类型和list map也都用上去了。接下来我们编写第一个测试。看代码。 @Test // 对复杂对象进行解码编码 public void testObject(){ User user = new User(); user.setUserId(10086); user.setUserName("张三"); user.setLockState(true); user.setUserType(UserType.VIP_USER); user.addAddress("上海"); user.addAddress("北京"); user.putFavor("tdd","当当网"); user.putFavor("java","Amazon"); // 保存转换之前的toString结果 String oldString = user.toString(); // 转换 byte[] data = ProtostuffUtil.serializer(user); User newUser = ProtostuffUtil.deserializer(data,User.class); // 保存转换之后的toString结果 String newString = newUser.toString(); assertEquals(oldString,newString); } 执行结果是通过。 然后编写第二个测试。 @Test // 对复杂对象的列表进行编码解码 public void testList(){ List<User> users = new ArrayList<User>(); User user = new User(); user.setUserId(10086); user.setUserName("张三"); user.setLockState(true); user.setUserType(UserType.VIP_USER); user.addAddress("上海"); user.addAddress("北京"); user.putFavor("tdd","Amazon"); users.add(user); User user2 = new User(); user2.setUserId(10086); user2.setUserName("张三"); user2.setLockState(true); user2.setUserType(UserType.VIP_USER); user2.addAddress("上海"); user2.addAddress("北京"); user2.putFavor("tdd","当当网"); user2.putFavor("java","Amazon"); users.add(user); // 保存转换之前的toString结果 String oldString = users.toString(); // 转换 byte[] data = ProtostuffUtil.serializer(users); List<User> newUsers = ProtostuffUtil.deserializer(data,users.getClass()); // 保存转换之后的toString结果 for(User u : newUsers){ System.out.println(u); } String newString = newUsers.toString(); assertEquals(oldString,newString); } 看到重复代码出现了,暂时不管它,重要的是先让测试通过。但是,执行结果是失败。 报出的错误是并发操作List时常出现的错误,就是通过外部强行修改了List的内部状态导致的。 我非常的不解,难道对象的List不能和byte[]相互转换?把List包到对象里面试试看。 @Test // 把List包到对象里进行编码解码 public void testUserPack(){ List<User> users = new ArrayList<User>(); User user = new User(); user.setUserId(10086); user.setUserName("张三"); user.setLockState(true); user.setUserType(UserType.VIP_USER); user.addAddress("上海"); user.addAddress("北京"); user.putFavor("tdd","Amazon"); users.add(user); UserListPack ulp = new UserListPack(); ulp.setUsers(users); // 保存转换之前的toString结果 String oldString = ulp.toString(); // 转换 byte[] data = ProtostuffUtil.serializer(ulp); UserListPack newUlp = ProtostuffUtil.deserializer(data,UserListPack.class); // 保存转换之后的toString结果 String newString = newUlp.toString(); assertEquals(oldString,newString); } UserListPack.java public class UserListPack { List<User> users; public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } @Override public String toString() { return "UserListPack [users=" + users + "]"; } } 执行的结果是成功。难道ProtoStuff不能转换List Map这样的数据?于是我又写了一个Map的测试。 @Test public void testMap(){ Map<Integer,User> uMap = new HashMap<Integer,User>(); User user = new User(); user.setUserId(10086); user.setUserName("张三"); user.setLockState(true); user.setUserType(UserType.VIP_USER); user.addAddress("上海"); user.addAddress("北京"); user.putFavor("tdd","Amazon"); uMap.put(1,user); User user2 = new User(); user2.setUserId(10086); user2.setUserName("张三"); user2.setLockState(true); user2.setUserType(UserType.VIP_USER); user2.addAddress("上海"); user2.addAddress("北京"); user2.putFavor("tdd","Amazon"); uMap.put(2,user); // 保存转换之前的toString结果 String oldString = uMap.toString(); // 转换 byte[] data = ProtostuffUtil.serializer(uMap); Map<Integer,User> newMap = ProtostuffUtil.deserializer(data,uMap.getClass()); // 保存转换之后的toString结果 String newString = newMap.toString(); assertEquals(oldString,newString); } 结果失败了。 注意画红框的那里,说明转换出来的Map是空的。看来ProtoStuff真的不能从byte[]转List Map了。 我又去搜了一下Java byte[] to List也没有好的解决办法。 ——更新:Google找到了————————– ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); try { @SuppressWarnings("unchecked") ArrayList<Object> list = (ArrayList<Object>) ois.readObject(); ... } finally { ois.close(); } 对应的List to byte[]代码是: ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = null; oos = new ObjectOutputStream(bos); oos.writeObject(mArrayList);//mArrayList is the array to convert byte[] buff = bos.toByteArray(); 这个代码我还没试。后续看看要不要干脆抛弃ProtoStuff,用上面的方法进行Object和byte[]互转算了。 ————————————————- 事到如今我知道ProtoStuffUtil只能转Object,所以只能先修改测试的代码,允许它抛异常,让测试先过掉了。 testMap的最后一句断言修改为 testList的@Test注解修改为 这样测试就都通过了。 测试重构这4个测试中的重复代码主要集中在User对象的创建及属性赋值和转换并比较前后字符串这两部分。 创建User的函数,只修改id和userName就足够了。 private User createUser(Integer userId,String userName){ User user = new User(); user.setUserId(userId); user.setUserName(userName); user.setLockState(true); user.setUserType(UserType.VIP_USER); user.addAddress("上海"); user.addAddress("北京"); user.putFavor("tdd","Amazon"); return user; } 执行比较的函数 private void doCompare(Object oldObj){ // 保存转换之前的toString结果 String oldString = oldObj.toString(); doCompare(oldObj,oldString); } private void doCompare(Object oldObj,String expectStr){ // 转换 byte[] data = ProtostuffUtil.serializer(oldObj); Object newObj = ProtostuffUtil.deserializer(data,oldObj.getClass()); // 保存转换之后的toString结果 String newString = newObj.toString(); assertEquals(expectStr,newString); } 重构以后的测试代码变得非常精简。 @Test // 对复杂对象进行解码编码 public void testObject(){ User user = createUser(10086,"张三"); doCompare(user); } 重构后4个测试依然是通过的。 总结初步尝试了TDD的流程,虽然没有走的很完整,但是也体验到了单元测试的好处。经过单元测试的类感觉能放心用了。为了避免盲目的信心,以后还要学习测试的相关理论,编写出更合理的测试用例。 下一次要实现某个类的时候再写一次TDD初探,体验完整的流程。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |