学习TDD(2)--实例:ProtoStuffUtil类的测试

前端之家收集整理的这篇文章主要介绍了学习TDD(2)--实例:ProtoStuffUtil类的测试前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

上篇讲了TDD的步骤和各种好处,俗话说的好,站在岸上是学不会游泳的。所以我们还是要拿个例子出来,实践一下TDD。

因为是第一次尝试,我想还是选个简单的例子,之前写的那个ProtoStuffUtil类就很不错。这个类主要负责对象跟byte[]之间的相互转换。可以参考http://www.jb51.cc/article/p-mhbggrwc-pm.html。其实这个类已经写好了,不太符合TDD的规范。但是体验一下还是可以的。

配套的代码已经上传http://download.csdn.net/detail/mrbcy/9748501

这个类虽然简单但是测试的流程还是很曲折

测试目标

因为这个类是负责对象跟byte[]之间的相互转换,所以我想从两个方面测试它:

  • 第一个是能够对复杂的对象进行正确的编码解码
  • 第二个是对复杂对象构成的List、Map进行正确的编码解码
  • @H_403_20@

    测试代码

    来看对象类代码

    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的最后一句断言修改assertEquals("{}",newString);

    testList的@Test注解修改@Test(expected = ConcurrentModificationException.class)

    这样测试就都通过了。

    测试重构

    这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初探,体验完整的流程。

猜你在找的设计模式相关文章