百日筑基第九天-单元测试Junit、Log4j 、Log4j 2

百日筑基第九天-单元测试Junit、Log4j 、Log4j 2

Junit

  • Junit是一个开源的 Java 单元测试框架。

  • 单元测试,就是针对最小的功能单元编写测试代码。在 Java 中,最小的功能单元就是方法,因此,对 Java 程序员进行单元测试实际上就是对 Java 方法的测试。

  • 单元测试可以确保你编写的代码是符合软件需求和遵循开发规范的。单元测试是所有测试中最底层的一类测试,是第一个环节,也是最重要的一个环节,是唯一一次能够达到代码覆盖率 100% 的测试,是整个软件测试过程的基础和前提。

使用:

1.导入依赖

xml 复制代码
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
</dependency>

2.测试方法中添加一组断言(假设有一个Factorial类且有待测方法fact)

java 复制代码
@Test
void fact() {
    assertEquals(1, Factorial.fact(1));
    assertEquals(2, Factorial.fact(2));
    assertEquals(6, Factorial.fact(3));
    assertEquals(100, Factorial.fact(5));
}

3.右键点「Run FactorialTest」,即可得到哪一行执行失败。

额外逻辑:

测试也支持添加前后逻辑:@BeforeEach、@AfterEach、@BeforeAll、@AfterAll

java 复制代码
class CalculatorTest {
    Calculator calculator;
    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }
    @AfterEach
    void tearDown() {
        calculator = null;
    }
    @Test
    void sub() {
        assertEquals(0,calculator.sub(1,1));
    }
    @Test
    void add() {
        assertEquals(2,calculator.add(1,1));
    }
}
  • @BeforeEachsetUp() 方法会在运行每个 @Test 方法之前运行;

  • @AfterEachtearDown() 方法会在运行每个 @Test 方法之后运行。

java 复制代码
public class DatabaseTest {
    static Database db;
    @BeforeAll
    public static void init() {
        db = createDb(...);
    }
    @AfterAll
    public static void drop() {
        ...
    }
}

All 通常用来初始化和销毁静态变量。

异常测试:

java 复制代码
public class Factorial {
    public static long fact(long n) {
        if (n < 0) {
            throw new IllegalArgumentException("参数不能小于 0");
        }
        long r = 1;
        for (long i = 1; i <= n; i++) {
            r = r * i;
        }
        return r;
    }
}

当n<0时怎么测试呢?答:assertThrows

java 复制代码
@Test
void factIllegalArgument() {
    assertThrows(IllegalArgumentException.class, new Executable() {
        @Override
        public void execute() throws Throwable {
            Factorial.fact(-2);
        }
    });
}

使用 Lambda 表达式:

c++ 复制代码
@Test
void factIllegalArgumentLambda() {
    assertThrows(IllegalArgumentException.class, () -> {
        Factorial.fact(-2);
    });
}

忽略测试:

@Disabled加在@Test注解之上

Log4j

打印日志,缺点是影响性能,但是日常开发中必不可少。(刚入职时前辈告诉我尽量多打日志)

  • Log4j 的一个好处是,不需要重新启动 Java 程序就可以调整日志的记录级别,非常灵活。可以通过 log4j.properties 文件来配置 Log4j 的日志级别、输出环境、日志文件的记录方式。

  • Log4j 还是线程安全的,可以在多线程的环境下放心使用。

第一步,在 pom.xml 文件中引入 Log4j 包:

xml 复制代码
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

第二步,在 resources 目录下创建 log4j.properties 文件,内容如下所示:

### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=debug.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =error.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

1)配置根 Logger,语法如下所示:

text 复制代码
log4j.rootLogger = [ level ] , appenderName, appenderName, ...

level 就是日志的优先级,从高到低依次是 ERROR、WARN、INFO、DEBUG。如果这里定义的是 INFO,那么低级别的 DEBUG 日志信息将不会打印出来。

appenderName 就是指把日志信息输出到什么地方,可以指定多个地方,当前的配置文件中有 3 个地方,分别是 stdout、D、E。

2)配置日志输出的目的地,语法如下所示:

text 复制代码
log4j.appender.appenderName = fully.qualified.name.of.appender.class  
log4j.appender.appenderName.option1 = value1  
...  
log4j.appender.appenderName.option = valueN

Log4j 提供的目的地有下面 5 种:

  • org.apache.log4j.ConsoleAppender:控制台
  • org.apache.log4j.FileAppender:文件
  • org.apache.log4j.DailyRollingFileAppender:每天产生一个文件
  • org.apache.log4j.RollingFileAppender:文件大小超过阈值时产生一个新文件
  • org.apache.log4j.WriterAppender:将日志信息以流格式发送到任意指定的地方

3)配置日志信息的格式,语法如下所示:

text 复制代码
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class  
log4j.appender.appenderName.layout.option1 = value1  
...  
log4j.appender.appenderName.layout.option = valueN

Log4j 提供的格式有下面 4 种:

  • org.apache.log4j.HTMLLayout:HTML 表格
  • org.apache.log4j.PatternLayout:自定义
  • org.apache.log4j.SimpleLayout:包含日志信息的级别和信息字符串
  • org.apache.log4j.TTCCLayout:包含日志产生的时间、线程、类别等等信息

自定义格式的参数如下所示:

  • %m:输出代码中指定的消息
  • %p:输出优先级
  • %r:输出应用启动到输出该日志信息时花费的毫秒数
  • %c:输出所在类的全名
  • %t:输出该日志所在的线程名
  • %n:输出一个回车换行符
  • %d:输出日志的时间点
  • %l:输出日志的发生位置,包括类名、线程名、方法名、代码行数,比如:method:com.itwanger.Log4jDemo.main(Log4jDemo.java:14)

第三步,写个使用 Demo:

java 复制代码
package com.itwanger;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

/**
 * @author 微信搜「沉默王二」,回复关键字 PDF
 */
public class Log4jDemo {
    private static final Logger logger = LogManager.getLogger(Log4jDemo.class);

    public static void main(String[] args) {
        // 记录debug级别的信息
        logger.debug("debug.");

        // 记录info级别的信息
        logger.info("info.");

        // 记录error级别的信息
        logger.error("error.");
    }
}

1)获取 Logger 对象

要使用 Log4j 的话,需要先获取到 Logger 对象,它用来负责日志信息的打印。通常的格式如下所示:

java 复制代码
private static final Logger logger = LogManager.getLogger(Log4jDemo.class);

2)打印日志

有了 Logger 对象后,就可以按照不同的优先级打印日志了。常见的有以下 4 种:

java 复制代码
Logger.debug() ;  
Logger.info() ;  
Logger.warn() ;  
Logger.error() ;

程序运行后会在 target 目录下生成两个文件,一个名叫 debug.log,内容如下所示:

text 复制代码
2020-10-20 20:53:27  [ main:0 ] - [ DEBUG ]  debug.
2020-10-20 20:53:27  [ main:3 ] - [ INFO ]  info.
2020-10-20 20:53:27  [ main:3 ] - [ ERROR ]  error.

另外一个名叫 error.log,内容如下所示:

text 复制代码
2020-10-20 20:53:27  [ main:3 ] - [ ERROR ]  error.

Log4j 2

Log4j 2 强在哪

1)在多线程场景下,Log4j 2 的吞吐量比 Logback 高出了 10 倍,延迟降低了几个数量级。

2)Log4j 2 可以减少垃圾收集器的压力。

3)支持 Lambda 表达式。

4)支持自动重载配置。

第一步,在 pom.xml 文件中添加 Log4j 2 的依赖:

xml 复制代码
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.5</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.5</version>
</dependency>

(这个 artifactId 还是 log4j,没有体现出来 2,而在 version 中体现,多少叫人误以为是 log4j)

第二步,来个最简单的测试用例:

java 复制代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {
    private static final Logger logger = LogManager.getLogger(Demo.class);
    public static void main(String[] args) {
        logger.debug("log4j2");
    }
}

**第三步,**在 resource 目录下增加 log4j2-test.xml 文件,内容如下所示:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

1)配置 appender,也就是配置日志的输出目的地。

有 Console,典型的控制台配置信息上面你也看到了,我来简单解释一下里面 pattern 的格式:

  • %d{HH:mm:ss.SSS} 表示输出到毫秒的时间
  • %t 输出当前线程名称
  • %-5level 输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补空格
  • %logger 输出 logger 名称,最多 36 个字符
  • %msg 日志文本
  • %n 换行

顺带补充一下其他常用的占位符:

  • %F 输出所在的类文件名,如 Demo.java
  • %L 输出行号
  • %M 输出所在方法名
  • %l 输出语句所在的行数, 包括类名、方法名、文件名、行数
  • %p 输出日志级别
  • %c 输出包名,如果后面跟有 {length.} 参数,比如说 %c{1.},它将输出报名的第一个字符,如 com.itwanger 的实际报名将只输出 c.i

2)配置 Loggers,指定 Root 的日志级别,并且指定具体启用哪一个 Appenders。

3)自动重载配置

Logback 支持自动重载配置,Log4j 2 也支持,那想要启用这个功能也非常简单,只需要在 Configuration 元素上添加 monitorInterval 属性即可。

text 复制代码
<Configuration monitorInterval="30">
...
</Configuration>

注意值要设置成非零,上例中的意思是至少 30 秒后检查配置文件中的更改。最小间隔为 5 秒。

Async 示例

除了 Console,还有 Async,可以配合文件的方式来异步写入,典型的配置信息如下所示:

xml 复制代码
<Configuration>
  <Appenders>
    <File name="DebugFile" fileName="debug.log">
      <PatternLayout>
        <Pattern>%d %p %c [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
    <Async name="Async">
      <AppenderRef ref="DebugFile"/>
    </Async>
  </Appenders>
  <Loggers>
    <Root level="debug">
      <AppenderRef ref="Async"/>
    </Root>
  </Loggers>
</Configuration>

把这个 Async 加入到 Appenders:

xml 复制代码
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        <File name="DebugFile" fileName="debug.log">
            <PatternLayout>
                <Pattern>%d %p %c [%t] %m%n</Pattern>
            </PatternLayout>
        </File>
        <Async name="Async">
            <AppenderRef ref="DebugFile"/>
        </Async>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="Async"/>
        </Root>
    </Loggers>
</Configuration>
相关推荐
好奇的菜鸟44 分钟前
如何在 Logback 和 Log4j 中获取日志:一个开发者指南
单元测试·log4j·logback
不要飞升1 小时前
百日筑基第十一天-看看SpringBoot
java·spring boot·后端·实习
好奇的菜鸟15 小时前
深入理解SLF4J与Logback以及Log4j的关系
单元测试·log4j·logback
星寂樱易李19 小时前
LUA 语言中subtree 的使用教程
开发语言·junit·lua
serve the people1 天前
redis 消息订阅命令
数据库·redis·junit
程序员古德1 天前
“论单元测试方法及应用”精选范文,软考高级论文,系统架构设计师论文
系统架构·单元测试
java6666688882 天前
Java中的单元测试与集成测试最佳实践
java·单元测试·集成测试
MJH8272 天前
接口测试流程及测试点!
自动化测试·软件测试·selenium·单元测试·pytest·接口测试·压力测试
初眸࿐2 天前
美团实习—后端开发凉经
java·功能测试·算法·leetcode·贪心算法·单元测试