其他测试框架 自定义自动化测试日志框架-Log4Reports

Alan · 2017年03月09日 · 最后由 Alan 回复于 2017年03月10日 · 1986 次阅读

市面上最受欢迎的 Java 日志组件可能要属 log4j 了,log4j 灵活丰富的配置可以帮助开发人员全方位地掌控日志信息。然而对于软件自动化测试来说,无论是 log4j,还是 Junit 、testNG 等测试框架下自动生成的日志和报告,对结果统计和数据展现的表现力就没那么优秀了,但是 log4j 它详尽的 log 信息又是我们需要的,鉴于此我在这里将 log4j 和拥有图表统计的 extentReports 日志框架结合在一起,从而满足对于不同的需求,选择不同的日志框架。
对于 extentReports 框架做一个简单的介绍,它是国外大牛 Anshoo Arora 写的一个开源报告框架,项目开源代码地址:https://github.com/anshooarora/extentreports ,对于 extentReports 的使用大家可以参考官网 (http://extentreports.com ) 的 API 和例子去实现(我最开始使用的时候全都是免费的,现在已出付费版了,真羡慕国外随便搞个工具(关键是人家还开源)都可以赚钱,在国内分分钟给你山寨出来一大堆),闲话不多说,下面正式进入框架的功能和实现步骤:

一、框架说明
Log4Reports 框架其实包含了:log4j + extentReports + TestNG,框架封装了 log4j 和 extentReports 两套日志框架功能,并基于 TestNG 创建了 BaseCase 类,用于组织和管理测试用例。Log4Reports 框架已上传到 github(https://github.com/AlanYangs/Log4Reports ),欢迎大家使用~

二、创建 log 配置类 LogConfig
LogConfig 类主要是用于初始化 log 文件路径及配置一些 log 的参数,采用 Builder 模式实例化对象(主要作用为给变量赋值)

public class LogConfig{
        /**日志类型 ,默认为extentreports, 0 = extentreports, 1 = log4j **/
    public static int logType = 0;
    /**失败重跑次数,默认0,失败不重跑**/
    public static int retryTimes = 0; 
        /**日志存放路径,最好是一个共享路径**/
    public static String sharedFolder = System.getProperty("user.dir");
    /**如需发送邮件,请配置SMTP host,比如163邮箱:smtp.163.com**/
    public static String smtpHost;
        /**发送邮箱的账号**/
    public static String sender;
    /**发送邮箱的账号密码**/
    public static String sendPassword;
    /**邮件接收者账号**/
    public static String receivers;
        /**邮件主题,默认:"Test Report Generated By Log4Reports"**/
    public static String subject;

    private String sharedPath;
    private String log4jFolder;
    private String log4jPath;
    private String extentFolder;
    private String extentLogPath;
    private String snapshotFolder;

    public LogConfig(Builder builder) {
        LogConfig.logType = builder.logType;
        LogConfig.retryTimes = builder.retryTimes;
        LogConfig.sharedFolder = builder.sharedFolder;
        LogConfig.smtpHost = builder.smtpHost;
        LogConfig.sender = builder.sender;
        LogConfig.sendPassword = builder.sendPassword;
        LogConfig.receivers = builder.receivers;
        LogConfig.subject = builder.subject;
                initLogPath(); //初始化日志文件夹
    }

    public static class Builder {
        private int logType = 0;
        private int retryTimes = 0;
        private String sharedFolder = "";

        private String smtpHost;
        private String sender;
        private String sendPassword;

        private String receivers;
        private String subject = "Test Report Generated By Log4Reports";

        public Builder(String sharedFolder){
            this.sharedFolder = sharedFolder;
        }

        public Builder setLogType(int logType) {
            this.logType = logType;
            return this;
        }

        public Builder setRetryTimes(int retryTimes) {
            this.retryTimes = retryTimes;
            return this;
        }

        public Builder setSmtpHost(String smtpHost) {
            this.smtpHost = smtpHost;
            return this;
        }

        public Builder setSender(String sender) {
            this.sender = sender;
            return this;
        }


        public Builder setSendPassword(String sendPassword) {
            this.sendPassword = sendPassword;
            return this;
        }

        public Builder setReceivers(String receivers) {
            this.receivers = receivers;
            return this;
        }

        public Builder setSubject(String subject) {
            this.subject = subject;
            return this;
        }

                public LogConfig build(){
                  return new LogConfig(this);
                }
    }
        private void initLogPath(){
        sharedPath =  CommonUtil.getFileName(LogConfig.sharedFolder);
        log4jFolder = sharedPath + "\\logs\\";
        log4jPath = sharedPath + "\\logs\\output.log";
        extentFolder = sharedPath + "\\reports\\";
        extentLogPath = sharedPath + "\\reports\\reports.html";
        snapshotFolder = sharedPath + "\\reports\\snapshot\\";

        File file;
        if(LogConfig.logType == 0){    
            file = new File(extentFolder);
            if(!file.exists()) file.mkdir();
        }else{
            file = new File(log4jFolder);
            if(!file.exists())file.mkdir();
        }

        if(LogConfig.logType == 0 && new File(extentLogPath).exists() || 
                LogConfig.logType == 1 && new File(log4jPath).exists()){
            if(deleteDir(file)) file.mkdir();
            if(file.exists() && file.list().length == 0){
                System.out.println("初始化日志文件夹【成功】");
            }else{
                System.out.println("初始化日志文件夹【失败】");
            }
        }
    }

    /**
     * @return the log4jPath
     */
    public String getLog4jPath() {
        return log4jPath;
    }

    /**
     * @return the extentLogPath
     */
    public String getExtentLogPath() {
        return extentLogPath;
    }

    /**
     * @return the snapshotFolder
     */
    public String getSnapshotFolder() {
        return snapshotFolder;
    }
}

三、创建 log 操作类 LogUtil
LogUtil 是整个框架的核心之一,主要封装了 log4j 和 extentReports 框架的对象及操作方法,实现步骤如下:
1> LogUtil 的构造方法,基于不同 log 类型选择 log 方式

public class LogUtil {
    private Logger logger;
    private ExtentReports report;
    private ExtentTest testLog;

    private LogConfig logConfig;

        public LogUtil(LogConfig config, Object obj){
        this.logConfig = config;
        if(LogConfig.logType == 0){
            File configFile = new File(System.getProperty("user.dir") + "\\configs\\extent-config.xml");
            if(configFile.exists()) {
                report = new ExtentReports(config.getExtentLogPath(),false);
                report.loadConfig(configFile);        
            }        
            testLog = report.startTest(String.valueOf(obj));
        }else{
            logger = Logger.getLogger(String.valueOf(obj));
            Layout layout = new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} [%p] %c:%L : %m%n");  
            try {
                Appender appender = new FileAppender(layout, config.getLog4jPath());
                logger.addAppender(appender);
            } catch (IOException e) {
                e.printStackTrace();
            } 
        }
    }
  ……

2> 实现具体的 log 方法

public class LogUtil {
        ……
    public int failCount = 0;
    public int errorCount = 0;
    public int warnCount = 0;
    public int skipCount = 0;
        ……
    //info
    public void info(String info){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.INFO, info);
            System.out.println(info);
        }else{
            logger.info(info);
        }
    }

    //warning
    public void warn(String warning){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.WARNING, warning);
            System.out.println(warning);
        }else{
            logger.warn(warning);
        }
        warnCount++;
    }

    //error
    public void error(String error){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.ERROR, error + snapshot());
            System.out.println(error);
        }else{
            logger.error(error);
        }
        errorCount++;
    }

    //skip
    public void skip(String skip){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.SKIP, skip);
            System.out.println(skip);
        }
        skipCount++;
    }

    //unknow
    public void unknow(String unknow){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.UNKNOWN, unknow);
            System.out.println(unknow);
        }
    }

    //fatal
    public void fatal(String fatal){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.FATAL, fatal + snapshot());
            System.out.println(fatal);
        }else{
            logger.fatal(fatal);
        }
        failCount++;
    }

    //fail
    public void fail(Throwable throwable){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.FAIL, snapshot() + throwable);
            System.out.println(throwable);
        }else{
            logger.error(throwable);
        }
        failCount++;
    }

    //fail
    public void fail(String fail){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.FAIL, fail + snapshot());
            System.out.println(fail);
        }else{
            logger.error(fail);
        }
        failCount++;
    }

    //pass
    public void pass(String pass){
        if(LogConfig.logType == 0){
            testLog.log(LogStatus.PASS, pass);
            System.out.println(pass);
        }else{
            logger.info(pass);
        }
    }
    ……
}

四、使用 Log4Reports 框架
1> 创建测试类继承 Log4Reports 框架的 BaseCase 类,并实现 initConfig() 方法:

public class TestCase1 extends BaseCase {
        ……
        //实现BaseCase的initConfig()方法
        @Override
    public void initConfig() {
        // TODO Auto-generated method stub
        logConfig = new LogConfig.Builder("\\\\172.0.0.1\\SharedFolder") //设置共享目录
        .setLogType(0)      //设置日志类型
        .setRetryTimes(1) //设置失败重跑次数
        .setSmtpHost("smtp.163.com") //设置发送邮箱的smtp host
        .setSender("automation@163.com") //设置发送邮箱的账号
        .setSendPassword("123456) //设置发送邮箱的账号密码
        .setReceivers("alany@xx.com;alany2@xx.com") //邮件接收者账号,多个账号用分号隔开
        .setSubject("Test Log4Reports") //邮件主题
        .build();
        System.out.println("init log config finished.");
    }
}

2> 需要使用 TestNG 的@BeforeClass(BaseCase 中使用的注解包括:@BeforeSuite@AfterSuite@BeforeClass@AfterClass@BeforeMethod@AfterMethod 这 6 个)等注解时,需要先调用父类对应方法:

@BeforeClass
public void beforeClass() {
    super.beforeClass();
    /** 下面实现你自己的逻辑 **/
}
    @AfterClass
public void afterClass(){
          super.afterClass();
      /** 下面实现你自己的逻辑 **/
    }
@AfterMethod
public void afterMethod(ITestResult result) {
    super.afterMethod(result);
    /** 下面实现你自己的逻辑 **/
}

    /** 不是上述的6个注解,不用调用父类的方法 **/

3>使用@Test注解编写测试方法:

public class TestCase1 extends BaseCase{
    private int flag = 0;

    @BeforeClass
    public void beforeClass() {
        super.beforeClass();
        flag = 1;
    }

    @AfterMethod
    public void afterMethod(ITestResult result) {
        super.afterMethod(result);
        flag = flag >1 ? 1 : flag;
    }

    @Test
    public void test(){
        if(assertEquals(flag, 1, true)){
            flag ++;
        }
        Log.info("this is info log");
        Log.pass("this is pass log");    
    }

    @Test
    public void test2(){
        if(assertEquals(flag, 2, true)){
            flag --;
        }
        Log.info("this is info log----A");
        Log.error("this is error log---B");
        Log.warn("this is warning log----C");
        Log.pass("this is pass log----D");
        Log.fail("this is fail log----E");
    }

    @Override
    public void initConfig() {
        // TODO Auto-generated method stub
        logConfig = new LogConfig.Builder("\\\\172.0.0.1\\SharedFolder")
        .setLogType(0)
        .setRetryTimes(1)
        .setSmtpHost("smtp.163.com")
        .setSender("automation@163.com")
        .setSendPassword("123456")
        .setReceivers("alany@XX.com")
        .setSubject("Test Log4Reports")
        .build();
        System.out.println("init log config finished.");
    }
}

4> 邮件报告效果:

5> log4j 的 log 明细:

6> extentReports 框架生成的报告明细:

五、框架 jar 包:

Log4Reports_1.0.0.jar

原文链接:http://www.jianshu.com/p/2c3fd46e2357

共收到 2 条回复 时间 点赞
  1. 排版不够好,没有完全遵守 markdown
  2. 无参与不推广,我不喜欢一上来都没热络就来推广自己公众号的人。
Alan #2 · 2017年03月10日 Author

OK,首次发帖不熟悉规则,请多包涵,多谢 LS 编辑提醒~

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册