欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java单元测试

程序员文章站 2022-06-05 12:12:04
...

在阿里巴巴Java开发手册,对于单元测试有如下描述,我大致抽象了下:

错误意识

  • 那是测试同学干的事情,单元测试代码是多余的!汽车的整体功能与各单元部件的测试正常与否是强相关?(认知需转变)
  • 单元测试代码不需要维护!一年半载后,那么单元测试几乎处于废弃状态。(跟随业务维护)
  • 单元测试与线上故障没有辩证关系!好的单元测试能够最大限度地规避线上故障(做比没做要好)

AIR 原则

单元测试在线上运行时候,感觉像空气(air)一样并不存在,但是在质量保证上却又十分关键。

  • A:Automatic(测试框架通常是定期执行的,执行过程必须完全自动化才有意义)
  • I:Independent(单元测试用例之间决不能互相调用,也不能依赖执行的先后次序)
  • R:Repeatable(单元测试是可以重复执行的,不能受到外界环境的影响)

BCDE 原则

  • B: Border,边界值测试,包括循环边界、特殊取值、特殊时间点,数据顺序。
  • C: Correct,正确的输入,并得到预期的结果。
  • D: Design,与设计文档相结合,来编写单元测试。
  • E: Error,单元测试的目的是证明程序有错,而不是证明程序无错。为了发现代码中潜在的错误,我们需要在编写测试用例时有一些强制的错误输入(如非法数据、异常流程、非业务允许输入等)来得到预期的错误结果

规范

强制

  • 粒度尽量小,最大至方法级别
  • 核心业务、核心应用、核心模块确保有单元测试
  • 代码必须写在如下工程目录:src/test/java

推荐

  • DAO层,Manager层,可重用度高的Service,都应该进行单元测试
  • 不手动操作数据库,请使用用程序准备符合业务的数据
  • 数据库相关的单元测试,可以设置自动回滚机制,不造脏数据
  • 不可测的代码需要做重构,不要为了要求测试而写不规范的测试代码
  • 在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例(UC)

业务代码

为了更方便地进行单元测试,业务代码应避免以下情况

  • 构造方法中做的事情过多
  • 存在过多的全局变量和静态方法
  • 存在过多的外部依赖
  • 存在过多的条件语句

Junit+Mockito

基于springBoot 1.5.19

一般我们开发都是使用springBoot,引入了类似如下的依赖:

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
 </dependency>
  • JUnit —用于 Java 应用程序的单元测试的事实上的标准。(4.x版本)
  • Spring Test 和 Spring Boot 测试—对 Spring Boot 应用程序的 Util 和集成测试支持。
  • Hamcrest —匹配器对象库(也称为约束或谓词)。
  • Mockito — Java 模拟框架。(1.x版本)
  • AssertJ —流畅的 assert 库。
  • JSONassert — JSON 的 assert 库。
  • JsonPath — JSON 的 XPath。

Junit

参考:https://github.com/junit-team/junit4/wiki/Getting-started

JUnit 是一个 Java 编程语言的单元测试框架。JUnit 在测试驱动的开发方面有很重要的发展,是起源于 JUnit 的一个统称为 xUnit 的单元测试框架之一。

相关注解

注解 说明
@Test(excepted==xx.class,timeout=毫秒数) 修饰一个方法为测试方法,excepted参数可以忽略某些异常类
@Before 在每一个测试方法被运行前执行一次
@BeforeClass 在所有测试方法执行前执行
@After 在每一个测试方法运行后执行一次
@AfterClass 在所有测试方法执行后执行
@Ignore 修饰的类或方法会被测试运行器忽略
@RunWith 更改测试运行器

常规用法

套件
public class TaskOneTest {
    @Test
    public void test() {
        System.out.println("Task one do.");
    }
}

public class TaskTwoTest {
    @Test
    public void test() {
        System.out.println("Task two do.");
    }
}

public class TaskThreeTest {
    @Test
    public void test() {
        System.out.println("Task Three.");
    }
}

// 1. 更改测试运行方式为 Suite
@RunWith(Suite.class) 
// 2. 将测试类传入进来
@Suite.SuiteClasses({TaskOneTest.class, TaskTwoTest.class, TaskThreeTest.class})
public class SuitTest {
    /**
     * 测试套件的入口类只是组织测试类一起进行测试,无任何测试方法,
     */
}
参数化测试

参数化测试允许开发人员使用不同的值反复运行同一个测试,遵循 5 个步骤来创建参数化测试

  • @RunWith(Parameterized.class)来注释 test 类
  • 创建一个由 @Parameters 注释的公共的静态方法,始化所有需要测试的参数对
  • 为测试类声明几个变量,分别用于存放期望值和测试所用数据
  • 创建一个公共的构造函数,为上述声明的几个变量赋值
  • 类有一个测试,它需要注解@Test到方法。
//1
@RunWith(Parameterized.class)
public class FibonacciTest {
     //2
    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
                {0, 0}, {1, 1}, {2, 1}, {3, 2}, {4, 3}, {5, 5}, {6, 8}
        });
    }

    //3
    private int fInput, fExpected;

    //4
    public FibonacciTest(int input, int expected) {
        this.fInput = input;
        this.fExpected = expected;
    }

    //5
    @Test
    public void test() {
        assertEquals(fExpected, Fibonacci.compute(fInput));
    }
}

// 被测试的类的方法    
public class Fibonacci {
    public static int compute(int n) {
    	int result = 0;
    	
        if (n <= 1) { 
        	result = n; 
        } else { 
        	result = compute(n - 1) + compute(n - 2); 
        }
        
        return result;
    }
}

若参数为单个的还可以这样做:

@Parameters
public static Iterable<? extends Object> data() {
    return Arrays.asList("first test", "second test");
}

@Parameters
public static Object[] data() {
    return new Object[] { "first test", "second test" };
}

断言

junit 断言,表格里面和assertThat。

断言 说明
assertArrayEquals(expecteds, actuals) 查看两个数组是否相等。
assertEquals(expected, actual) 查看两个对象是否相等。类似于字符串比较使用的equals()方法。
assertNotEquals(first, second) 查看两个对象是否不相等。
assertNull(object) 查看对象是否为空。
assertNotNull(object) 查看对象是否不为空。
assertSame(expected, actual) 查看两个对象的引用是否相等。类似于使用“==”比较两个对象。
assertNotSame(unexpected, actual) 查看两个对象的引用是否不相等。类似于使用“!=”比较两个对象。
assertTrue(condition) 查看运行结果是否为true。
assertFalse(condition) 查看运行结果是否为false。
assertThat与Matcher

包括两个包org.hamcrest.CoreMatchers和org.hamcrest.Matchers

  1. org.hamcrest.CoreMatchers
  • assertThat( number, allOf( greaterThan(10), lessThan(15) ) ):相当于“与”(&&),即allOf中条件都必须成立
  • assertThat( number, anyOf( greaterThan(30), lessThan(1) ) ):相当于“或”(||),即只要anyOf中有一个条件成立就可以了
  • assertThat( number, anything() ):无论什么条件,永远为true
  • assertThat( string, is( “engine” ) )和assertThat( string, equalTo( expectedValue ) ):两个作用是一样的,只是写法不同,都是Object的equals方法
  • assertThat( string, not( “engine” ) ):not匹配符和is匹配符正好相反
  1. org.hamcrest.Matchers

(1)字符串相关匹配符

  • assertThat( string, containsString( “engine” ) ):包含子字符串”engine”则测试通过
  • assertThat( string, endsWith( “engine” ) ):以子字符串”engine”结尾则测试通过
  • assertThat( string, startsWith( “engine” ) ):以子字符串”engine”开始则测试通过
  • assertThat( testedString, equalToIgnoringCase( “engine” ) ); 忽略大小写的情况
  • assertThat( testedString, equalToIgnoringWhiteSpace( “engine” ) ):忽略头尾的任意个空格
  • assertThat( string, is( “engine” ) ):判断两个字符串相等

(2)数值相关匹配符

  • assertThat( number, closeTo( 10.0, 0.1 ) ):在10.0±0.1范围之内则测试通过
  • assertThat( number, greaterThan(11) ):大于11则测试通过
  • assertThat( number, lessThan (11) ):小于11则测试通过
  • assertThat( number, greaterThanOrEqualTo (11) ):大于等于11则测试通过
  • assertThat( number, lessThanOrEqualTo (11) ):小于等于11则测试通过

(3)collection相关匹配符

  • assertThat( map, hasEntry( “key”, “value” ) ):mapObject含有一个键值为”key”对应元素值为”value”测试通过
  • assertThat( list, hasItem ( “element” ) ):iterableObject含有元素“element”项则测试通过
  • assertThat( map, hasKey ( “key” ) ):mapObject含有键值“key”则测试通过
  • assertThat( map, hasValue ( “key” ) ):mapObject含有元素值“value”则测试通过
public class AssertTests {
  @Test
  public void testAssertArrayEquals() {
    byte[] expected = "trial".getBytes();
    byte[] actual = "trial".getBytes();
    org.junit.Assert.assertArrayEquals("failure - byte arrays not same", expected, actual);
  }

  @Test
  public void testAssertEquals() {
    org.junit.Assert.assertEquals("failure - strings are not equal", "text", "text");
  }

  @Test
  public void testAssertFalse() {
    org.junit.Assert.assertFalse("failure - should be false", false);
  }

  @Test
  public void testAssertNotNull() {
    org.junit.Assert.assertNotNull("should not be null", new Object());
  }

  @Test
  public void testAssertNotSame() {
    org.junit.Assert.assertNotSame("should not be same Object", new Object(), new Object());
  }

  @Test
  public void testAssertNull() {
    org.junit.Assert.assertNull("should be null", null);
  }

  @Test
  public void testAssertSame() {
    Integer aNumber = Integer.valueOf(768);
    org.junit.Assert.assertSame("should be same", aNumber, aNumber);
  }

  // JUnit Matchers assertThat
  @Test
  public void testAssertThatBothContainsString() {
    org.junit.Assert.assertThat("albumen", both(containsString("a")).and(containsString("b")));
  }

  @Test
  public void testAssertThathasItemsContainsString() {
    org.junit.Assert.assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
  }

  @Test
  public void testAssertThatEveryItemContainsString() {
    org.junit.Assert.assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }), everyItem(containsString("n")));
  }

  // Core Hamcrest Matchers with assertThat
  @Test
  public void testAssertThatHamcrestCoreMatchers() {
    assertThat("good", allOf(equalTo("good"), startsWith("good")));
    assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
    assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
    assertThat(7, not(CombinableMatcher.<Integer> either(equalTo(3)).or(equalTo(4))));
    assertThat(new Object(), not(sameInstance(new Object())));
  }

  @Test
  public void testAssertTrue() {
    org.junit.Assert.assertTrue("failure - should be true", true);
  }
}
相关标签: Java基础