大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

DBCompare小工具研发

测试过程中,经常涉及到数据库表记录的检查,比如案例执行完成后,需检查某几张表insert,update或delete是否正确。面对此类测试场景,测试人员一般都是交易前后分别把表记录导出,然后再做比对检查表记录是否正确。这种测试方法存在两个问题:

1、操作繁琐。
2、表记录过多时,纯靠眼力检查容易出错。

针对以上问题,我们稍微转换一下思路,让工具来简化测试工作,提升测试效率及准确度。下面笔者将以oracle数据库为例,详述DBCompare小工具的研发过程。
首先,笔者将工具的使用方法做简单描述,然后再跟各位分享工具的实现思路及相关代码。

数据比对工具.png

上图为小工具的界面展示,“环境”配置在config.properties文件,配置信息为用户名、密码、ip及端口信息,可灵活配置各个环境参数,如下图所示。
环境配置.png

选择环境,填写表名,然后获取索引;
(1)“输入索引”为选输框,获取索引失败的话,可手工输入索引;
(2)“查询条件”为选输框,如果“查询条件为空”,则查询全表记录作比较;否则只获取符合查询条件的记录作比较;
(3)点击“交易前查询”按钮;
(4)执行交易或测试脚本;
获取索引.png

交易前查询.png

(5)点击“交易后查询”按钮,自动打开交易前后比对文件。

  • 对于交易前和交易后都存在的记录,根据索引比对各字段是否一致,不一致的字段标注红色;
  • 对于交易前存在记录,交易后不存在记录(删除),标准玫瑰色;
  • 对于交易前不存在记录,交易后存在记录(新增),标准红色;
    交易前后记录比对文件.png

    交易前后记录比对文件.png

    以上就是工具的简单描述,接下来咱们来聊聊工具实现的思路。
  • 读取环境配置;
    /*
     * 获取环境配置信息
     */
    public static Map<String, String> getEnv() {
        Map<String, String> env_map = new HashMap<String, String>();
        InputStream in = null;
        Properties p = new Properties();
        try {
            in = new BufferedInputStream(new FileInputStream("config\\"
                    + "config.properties"));
            p.load(in);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Enumeration<Object> keys = p.keys();
        while (keys.hasMoreElements()) {
            String key = (String) keys.nextElement();
            env_map.put(key, p.getProperty(key));
        }
        System.out.println("env_map: " + env_map);
        return env_map;
    }
  • 链接数据库;
/*
     * 链接数据库
     */
    // @SuppressWarnings("finally") String env,String user,String password
    public static Connection oracle() {
        try {
            /*
             * 获取环境参数
             * upi[0]=用户名;
             * upi[1]=密码;
             * upi[2]=IP地址;
             */
            String usPassIp = envVar_map.get(comBoxItem).trim(); //获取用户名,密码,ip
            String upi[] = usPassIp.split(",");// 分割字符串得到数组
            for (int i = 0; i < upi.length; i++) {
                System.out.println(upi[i]);
            }
            
            Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
            System.out.println("开始尝试连接数据库!");
            String url = "jdbc:oracle:" + "thin:@"+ upi[2];  //10.232.132.6:1521:p2b";
            String user = upi[0];// 用户名,系统默认的账户名
            String password = upi[1];// 你安装时选设置的密码
            con = DriverManager.getConnection(url, user, password);// 获取连接
            System.out.println("连接成功!");
            System.out.println("con前:" + con);

            return con;
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e.getMessage(), "message",
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }
    }
  • 获取索引;
/*
     * 获取主键字段
     */
    public static ArrayList<String> getKeyWord(String tableName)
            throws SQLException {
        try {
            ResultSet rs = null;
            
            con = oracle();
            if(con == null){  //防止oracle链接报错时,连续提示两次错误框;
                return null;
            }
            
            DatabaseMetaData dbMeta = con.getMetaData();
            

            rs = dbMeta.getPrimaryKeys(null, null, tableName.toUpperCase());

            ArrayList<String> keyWord = new ArrayList<String>();
            while (rs.next()) {
                keyWord.add(rs.getObject(4).toString());
            }
            System.out.println("主键: " + keyWord);

            if (rs != null)
                rs.close();
            if (con != null)
                con.close();
            System.out.println("数据库连接已关闭!");
            System.out.println("con后(主键):" + con);
            return keyWord;
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e.getMessage(), "message",
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }

    }
  • 交易前查询按钮主要动作是把数据库表的记录临时存放到List<Map<String, String>>,后续再跟交易后查询的记录做比对。
/*
     * 获取交易前数据
     */
    public static List<Map<String, String>> getTradeBef(String tableName,
            String queryConditon) throws SQLException {
        try {
            ResultSet result = null;// 创建一个结果集对象
            PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
            String sql = "";

            List<Map<String, String>> tradeBef = new ArrayList<Map<String, String>>();
            ArrayList<String> tableHead = new ArrayList<String>();

            tableead = getTableHead(tableName);
            if(tableHead == null){  //防止oracle链接报错时,连续提示两次错误框;
                return null;
            }
            
            if (!queryConditon.equals(""))
                sql = "select * from " + tableName + " where " + queryConditon;
            else
                sql = "select * from " + tableName;

            con = oracle();
            if(con == null){  //防止oracle链接报错时,连续提示两次错误框;
                return null;
            }
            
            pre = con.prepareStatement(sql);// 实例化预编译语句
            result = pre.executeQuery();

            while (result.next()) {
                Map<String, String> map = new HashMap<String, String>();
                for (int i = 0; i < tableHead.size(); i++) {
                    map.put(tableHead.get(i),
                            result.getString(tableHead.get(i)));
                }

                tradeBef.add(map); // 交易前
            }
            System.out.println("交易前map: " + tradeBef);
            /*
             * System.out.println("交易前第一个map的商户名: " +
             * tradeBef.get(0).get("MERCHANT_NAME"));
             */

            if (result != null)
                result.close();
            if (pre != null)
                pre.close();
            if (con != null)
                con.close();
            System.out.println("数据库连接已关闭!");
            System.out.println("con后(交易前):" + con);

            JOptionPane.showMessageDialog(null, "交易前查询成功", "message", 1); // 提示交易前查询成功

            return tradeBef;
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e.getMessage(), "message",
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }

    }
  • 交易后查询按钮的主要动作是把数据库的记录跟交易前获取list<map>做对比,然后写入excel结果文件,对前后不一致的字段标注相应的颜色。
    /*
     * 获取交易后数据
     */
    public static List<Map<String, String>> getTradeAft(String tableName,
            String queryConditon) throws SQLException {
        try {
            ResultSet result = null;// 创建一个结果集对象
            PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
            String sql = "";

            List<Map<String, String>> tradeAft = new ArrayList<Map<String, String>>();
            ArrayList<String> tableHead = new ArrayList<String>();

            tableHead = getTableHead(tableName);
            if(tableHead == null){  //防止oracle链接报错时,连续提示两次错误框;
                return null;
            }

            if (!queryConditon.equals(""))
                sql = "select * from " + tableName + " where " + queryConditon;
            else
                sql = "select * from " + tableName;

            con = oracle();
            if(con == null){  //防止oracle链接报错时,连续提示两次错误框;
                return null;
            }
            
            pre = con.prepareStatement(sql);// 实例化预编译语句
            result = pre.executeQuery();

            while (result.next()) {
                Map<String, String> map = new HashMap<String, String>();
                for (int i = 0; i < tableHead.size(); i++) {
                    map.put(tableHead.get(i),
                            result.getString(tableHead.get(i)));
                }

                tradeAft.add(map); // 交易前
            }
            System.out.println("交易后map: " + tradeAft);
            /*
             * System.out.println("交易后第一个map的商户名: " +
             * tradeAft.get(0).get("MERCHANT_NAME"));
             */

            if (result != null)
                result.close();
            if (pre != null)
                pre.close();
            if (con != null)
                con.close();
            System.out.println("数据库连接已关闭!");
            System.out.println("con后(交易后):" + con);

            return tradeAft;
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e.getMessage(), "message",
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }

    }
/*
     * 交易前后数据对比
     */
    public static String compare(List<Map<String, String>> tradeBef,
            List<Map<String, String>> tradeAft, String tableName)
            throws SQLException, WriteException, IOException, BiffException {

        String keyValue1 = "";
        String keyValue2 = "";
        String keyValue3 = "";
        String keyValue4 = "";
        ArrayList<String> tableHead = new ArrayList<String>();
        tableHead = getTableHead(tableName);
        if(tableHead == null){  //防止oracle链接报错时,连续提示两次错误框;
            return "";
        }

        String filePath = cOutputFile(tableHead); // 创建文件

        ArrayList<String> list1 = new ArrayList<String>(); // 临时存储交易前的记录
        ArrayList<String> list2 = new ArrayList<String>(); // 临时存储交易后的记录
        /****************************************************************************/
        /*
         * 1、处理tradeBef存在但tradeAft不存在的记录; 2、处理tradeBef和tradeAft都存在的记录;
         */
        for (int i = 0; i < tradeBef.size(); i++) {
            keyValue1 = "";
            for (int a = 0; a < key.size(); a++) { // 获取交易前记录主键的值
                keyValue1 += tradeBef.get(i).get(key.get(a));
            }
            System.out.println("keyValue1: " + keyValue1);

            for (int k = 0; k < tableHead.size(); k++) {
                list1.add(tradeBef.get(i).get(tableHead.get(k))); // 临时存储交易前的记录
            }

            for (int j = 0; j < tradeAft.size(); j++) { // 遍历交易后的记录
                keyValue2 = "";
                for (int b = 0; b < key.size(); b++) { // 获取交易后记录主键的值
                    keyValue2 += tradeAft.get(j).get(key.get(b));
                }
                System.out.println("keyValue2: " + keyValue2);

                if (keyValue1.equals(keyValue2)) { // 记录在tradeBef和tradeAft都存在
                    for (int k = 0; k < tableHead.size(); k++) {
                        list2.add(tradeAft.get(j).get(tableHead.get(k))); // 临时存储交易后的记录
                    }
                    break;
                }
            }

            if (list2.size() == 0) { // tradeBef存在,tradeAft不存在
                wOutputFile(filePath, list1, null, tableHead); // 写结果文件
            } else {
                wOutputFile(filePath, list1, list2, tableHead);
            }
            System.out.println("交易前list1: " + list1);
            System.out.println("交易后list2: " + list2);
            list1.clear(); // 清除临时记录
            list2.clear();
        }

        /****************************************************************************/
        /*
         * 1、处理tradeBef不存在但tradeAft存在的记录;
         */
        for (int i = 0; i < tradeAft.size(); i++) {
            keyValue3 = "";
            for (int a = 0; a < key.size(); a++) { // 获取交易后记录主键的值
                keyValue3 += tradeAft.get(i).get(key.get(a));
            }

            for (int k = 0; k < tableHead.size(); k++) {
                list2.add(tradeAft.get(i).get(tableHead.get(k))); // 临时存储交易后的记录
            }

            for (int j = 0; j < tradeBef.size(); j++) { // 遍历交易前的记录
                keyValue4 = "";
                for (int b = 0; b < key.size(); b++) { // 获取交易前记录主键的值
                    keyValue4 += tradeBef.get(j).get(key.get(b));
                }

                if (keyValue3.equals(keyValue4)) {
                    list2.clear(); // 记录在tradeBef和tradeAft都存在,则清空list
                    break; // 跳出循环
                }
            }
            if (list2.size() != 0) { // 交易后记录list非空
                wOutputFile(filePath, null, list2, tableHead);
                System.out.println("tradeBef不存在但tradeAft存在 list2: " + list2);
            }
            list2.clear();
        }
        return filePath;

    }
    /*
     * cOutputFile方法创建对比结果文件,入参为表头list;
     */
    public static String cOutputFile(ArrayList<String> list)
            throws IOException, WriteException {

        String temp_str = "";
        Date dt = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        temp_str = sdf.format(dt); // 获取时间戳

        // 相对路径默认为 D:\Progrom Files\apache-jmeter-3.1\bin "D:\\\\" +
        String filepath = "result\\" + "比对结果" + "_output_" + "_" + temp_str
                + ".xls"; // 以时间戳命名结果文件,确保唯一

        File output = new File(filepath);

        if (!output.isFile()) {
            output.createNewFile(); // 如果指定文件不存在,则新建该文件
            WritableWorkbook writeBook = Workbook.createWorkbook(output);
            WritableSheet Sheet = writeBook.createSheet("比对结果", 0); // createSheet(sheet名称,第几个sheet)

            for (int i = 0; i < list.size(); i++) {
                // CellView cellView = new CellView();
                // cellView.setAutosize(true); //设置自动大小
                // Sheet.setColumnView(i, cellView);//根据内容自动设置列宽

                Sheet.setColumnView(i, 25); // 设置列宽度setColumnView(列号,宽度)
                WritableFont headfont = new WritableFont(
                        WritableFont.createFont("宋体"), 11, WritableFont.BOLD); // 字体样式
                WritableCellFormat headwcf = new WritableCellFormat(headfont);
                headwcf.setBackground(Colour.GRAY_25); // 灰色颜色
                headwcf.setAlignment(Alignment.CENTRE); // 设置文字居中对齐方式;
                headwcf.setVerticalAlignment(VerticalAlignment.CENTRE); // 设置垂直居中;

                Label labe00 = new Label(i, 0, list.get(i), headwcf); // Label(列号,行号,内容)
                Sheet.addCell(labe00);
            }

            writeBook.write();
            writeBook.close();
        }
        return filepath;
    }

    /*
     * wOutputFile方法写对比结果文件,入参为 文件路径、交易前记录list、交易后记录list、表头list;
     */
    public static void wOutputFile(String filepath, ArrayList<String> list1,
            ArrayList<String> list2, ArrayList<String> tableHead)
            throws IOException, RowsExceededException, WriteException,
            BiffException {

        File output = new File(filepath);
        InputStream instream = new FileInputStream(filepath);
        Workbook readwb = Workbook.getWorkbook(instream);
        WritableWorkbook wbook = Workbook.createWorkbook(output, readwb); // 根据文件创建一个操作对象

        WritableSheet readsheet = wbook.getSheet(0);
        int rsRows = readsheet.getRows(); // 获取Sheet表中所包含的总行数

        WritableFont font = new WritableFont(WritableFont.createFont("宋体"), 10,
                WritableFont.NO_BOLD);// 字体样式

        Cell cell1 = readsheet.getCell(0, rsRows);
        if (cell1.getContents().equals("")) {
            for (int m = 0; m < tableHead.size(); m++) {

                WritableCellFormat wcf = new WritableCellFormat(font); // 字体样式设置

                if (list1 != null && list2 != null) { // 交易前后记录都存在
                    if (list1.get(m) != null && list2.get(m) != null) {// 某些字段可能为null,需加判断
                        if (!list1.get(m).toString()
                                .equals(list2.get(m).toString())) {
                            wcf.setBackground(Colour.RED); // 不一致字段标注红色
                        }
                    }
                    Label labetest1 = new Label(m, rsRows, list1.get(m), wcf);
                    readsheet.addCell(labetest1);
                    Label labetest2 = new Label(m, rsRows + 1, list2.get(m),
                            wcf);
                    readsheet.addCell(labetest2);
                }

                if (list1 != null && list2 == null) { // 交易前记录存在,交易后记录不存在
                    wcf.setBackground(Colour.ROSE); // 不一致字段标注红色
                    Label labetest1 = new Label(m, rsRows, list1.get(m), wcf);
                    readsheet.addCell(labetest1);
                }
                if (list1 == null & list2 != null) { // 交易前记录不存在,交易后记录存在
                    wcf.setBackground(Colour.RED); // 不一致字段标注红色
                    Label labetest2 = new Label(m, rsRows, list2.get(m), wcf);
                    readsheet.addCell(labetest2);
                }
            }
        }
        wbook.write();
        wbook.close();
    }

至此,DBCompare小工具开发完成,觉得对工作有帮助的童鞋,赶紧尝试一下吧。