如何对异常进行单元测试?


问题内容

如您所知,异常情况下会引发异常。那么如何模拟这些异常呢?我觉得这是挑战。对于此类代码段:

public String getServerName() {
    try {

        InetAddress addr = InetAddress.getLocalHost();
        String hostname = addr.getHostName();
        return hostname;
    }
    catch (Exception e) {
        e.printStackTrace();
        return "";
    }
}

有人有好主意吗?


问题答案:

其他答案已经解决了如何编写用于检查是否引发异常的单元测试的一般问题。但是我认为您的问题实际上是在询问如何获取代码以首先引发异常。

以您的代码为例。getServerName()在简单的单元测试的环境中,很难在内部引发异常。问题是,为了使异常发生,代码(通常)需要在网络中断的计算机上运行。安排在单元测试中进行该操作可能是不可能的……您需要在运行测试之前故意对计算机进行错误配置。

所以答案是什么?

  1. 在某些情况下,简单的答案只是采取务实的决定,而不是全面测试。您的方法就是一个很好的例子。从代码检查中应该清楚该方法的实际作用。测试它不会证明任何东西(除了下面的内容**)。您要做的只是提高测试次数和测试覆盖率,这两个都不是项目 目标

  2. 在其他情况下,将生成异常的低级代码分离出来并使其成为一个单独的类可能是明智的。然后,要测试更高级别代码对异常的 处理 ,可以用将引发所需异常的模拟类替换该类。

考虑到这种“治疗”,这是您的示例。(这有点做作…)

public interface ILocalDetails {
    InetAddress getLocalHost() throws UnknownHostException;
    ...
}

public class LocalDetails implements ILocalDetails {
    public InetAddress getLocalHost() throws UnknownHostException {
        return InetAddress.getLocalHost();
    }
}

public class SomeClass {
    private ILocalDetails local = new LocalDetails();  // or something ...
    ...
    public String getServerName() {
        try {
            InetAddress addr = local.getLocalHost();
            return addr.getHostName();
        }
        catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
}

现在要对此进行单元测试,您将创建ILocalDetails接口的“模拟”实现,该接口的getLocalHost()方法将在适当的条件下引发所需的异常。然后,您为创建一个单元文本SomeClass.getServerName(),安排的实例SomeClass使用您的“
mock”类的实例而不是普通的实例。(最后一点可以使用模拟框架,通过setterlocal属性公开或使用反射API来完成。)

显然,您需要修改代码以使其可测试。而且您可以做的事情有局限性…例如,您现在无法创建单元测试来使真正的LocalDetails.getLocalHost()方法引发异常。您需要根据情况进行判断,以决定是否值得这样做。也就是说,单元测试的好处是否超过了以这种方式使类可测试的工作(以及额外的代码复杂性)。(事实上​​,在此static方法的底部有一个方法是很大一部分问题。)


**这里 一个假设点这种测试。在您的示例中,原始代码捕获到异常并返回空字符串这一事实 可能
是一个错误……这取决于方法API的指定方式……并且假设的单元测试将对其进行提取。但是,在这种情况下,该错误是如此明显,以至于您在编写单元测试时会发现它!并且假设您在发现错误时就对其进行了修复,那么单元测试将变得有些多余。(您不会期望有人重新设置此特定错误……)