Java单元测试

在阿里巴巴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);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值