Java - 如何测试永远不会发生的异常?

时间:2017-06-17 15:28:01

标签: java unit-testing

我有Utilsmethod,当给定数据不正确时会抛出异常。

我还Service使用此method,但数据始终以在调用期间正确的方式生成。数据由另一个utils类生成。

我知道我应该从Utils类中抛出此异常 - 但我不能从Service抛出它 - 所以我必须抓住它。

如何测试,模拟此异常? 对此数据的所有操作都采用私有方法。

我想避免使用PowerMock,因为我听说它是​​设计糟糕的标志。 所以问题是,如何在良好的设计中实现这一点?

2 个答案:

答案 0 :(得分:3)

根据您的描述,它看起来像这样:

class Service {

    public void someMethod() {
        Data data = AnotherUtils.getData();

        try {
            Utils.method(data); // exception never thrown
        } catch(Exception e) {
            // how to test this branch?
        }
    }

}

目标是这样的:

interface DataProvider {
    Data getData();
}

interface DataConsumer {
    void method(Data data);
}

class Service {
    private final DataProvider dataProvider;
    private final DataConsumer dataConsumer;

    public Service(DataProvider dataProvider, DataConsumer dataConsumer) {...}

    public void someMethod() {
        Data d = dataProvider.getData();

        try {
            dataConsumer.method(data);
        } catch(Exception e) {

        }
    }
}

此技术称为dependency injection

然后,在测试时,您可以简单地为 返回错误数据的DataProvider接口提供模拟实现:

@Test(expected=Exception.class)
public void myTest() {
    DataProvider badDataProvider = () -> new BadData(); // Returns faulty data

    Service service = new Service(badDataProvider, Utils.getConsumer());
    service.someMethod(); // boom!
}

对于非测试代码,您可以简单地将已有的utils类包装在这些接口中:

class AnotherUtils {
    public static Data getData() {...}

    public static DataProvider getProvider() {
        return AnotherUtils::getData;
    }
}

...

Service service = new Service(AnotherUtils.getProvider(), Utils.getConsumer());

答案 1 :(得分:1)

这是一种您希望引入依赖注入的方法,但无论出于何种原因您都不想更改遗留代码。

假设你有一些像这样的静态实用方法:

class Utils{
    public static Something aMethod(SomethingElse input) throws AnException{
          if(input.isValid())
              return input.toSomething();
          throw new AnException("yadda yadda");
    }
}

你有一个使用该实用方法的类。您仍然可以使用FunctionalInterface注入它。

@FunctionalInterface
interface FunctionThrowsAnException<K,V> {
    V apply(K input) throws AnException;
}

class Service {
    private final FunctionThrowsAnException<SomethingElse,Something> func;

    Service(FunctionThrowsAnException<SomethingElse,Something> func){
        this.func = func;
    }

    Something aMethod(SomethingElse input){
         try{
            return func.apply(input);
         }catch(AnException ex){
            LOGGER.error(ex);
         }
    }
}

然后像这样使用它:

new Service(Utils::aMethod).aMethod(input);

测试它:

new Service(x -> { throw new AnException("HA HA"); }).aMethod(input);