【测试之道】深入探索:单元测试之异常测试

孤人 提交于 2020-04-07 10:26:03

相关文章

预期异常

如果我们测试知道这个方法是要抛出异常才是我们所期望的,不然就是测试失败,我们要如何验证呢?验证码完成通常是重要的,但是确保代码在特殊情况下的行为(如:异常,状态,超时,etc)同样重要。 举个例子:

new ArrayList<Object>().get(0);

这无疑无抛出IndexOutOfBoundsException,简称IOOBE。在@Test注解中,存在一个excepted参数,可以知道方法会抛出我们预期的异常。如果我们想去验证ArraryList 抛出正确的异常,我们可以这样写:

@Test(expected = IndexOutOfBoundsException.class) 
public void empty() { 
     new ArrayList<Object>().get(0); 
}

这个excepted 参数应该是我们所应该关心的,如果这个执行方法抛出IOOBE,那么这个方法将会通过测试。是不是用起来很简单,很爽。然而,这个方法并不是如理想中那么简单,万能的。其实官方推荐的是使用 ExceptedException rule, 来实现。

下面我举一个比较极端的一个例子吧! 如果我要测试一个方法,我遇见他会是超时的,终会抛出超时异常: 我们可以这样写:

    @Test(timeout = 1000,expected = TestTimedOutException.class)
    public void testA() {
        System.out.println("first");
        for (;;){}// 必然会抛出超时异常
    }

我预测,他终会抛出异常超时,但是它仍然是无法通过测试的,这样的情况我们要如何做呢? 好吧,我们就试试project B, 按照官方推荐的试试:

    @Rule
    public ExpectedException thrown = ExpectedException.none();
    @Test(timeout = 1000)
    public void testB() {
        thrown.expect(TestTimedOutException.class);
        for(;;){}
    }

这样就能完好的通过测试。当然,@Test(timeout=1000) 也是可以用** @Rule public Timeout globalTimeout = Timeout.seconds(1000); ** 来替代的,后面会讲到,大家耐心一点哈!

深入探索异常测试(使用@Rule)

上面的方法对简单的情况很有用,但也有它的局限性。例如,您不能在异常中测试消息的值,或者在抛出异常后对域对象的状态进行测试(如订单业务的 下单状态、支付状态、退款状态这种比较常见),etc。

Try/Catch 实现

在JUnit3.x 是通过 Try/Catch 来实现的

@Test
public void testExceptionMessage() {
    try {
        new ArrayList<Object>().get(0);
        fail("Expected an IndexOutOfBoundsException to be thrown");
    } catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
        assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
    }
}

ExpectedException Rule

另外,使用ExpectedException rule . 此规则不仅可以指示您期望的异常情况,还可以指示预期的异常消息:

@Rule
public ExpectedException thr = ExpectedException.none();

@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
    List<Object> list = new ArrayList<Object>();
    // 预测异常
    thr.expect(IndexOutOfBoundsException.class);
    thr.expectMessage("Index: 0, Size: 0");
    list.get(0); // execution will never get past this line
}

也可以让你的expectmessage使用匹配器,这使你在你的测试更多的灵活性。一个例子:

thrown.expectMessage(Matchers.containsString("Size: 0"));

引入匹配器,需要org.hamcrest 的jar 包。 此外,您可以使用匹配检查异常,如果它具有嵌入式状态你想验证。例如:

import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;

import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class TestExy {
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void shouldThrow() {
        TestThing testThing = new TestThing();
        thrown.expect(NotFoundException.class);
        thrown.expectMessage(startsWith("some Message"));
        thrown.expect(hasProperty("response", hasProperty("status", is(404))));
        testThing.chuck();
    }

    private class TestThing {
        public void chuck() {
            Response response = Response.status(Status.NOT_FOUND).entity("Resource not found").build();
            throw new NotFoundException("some Message", response);
        }
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!