我有Utils
类method
,当给定数据不正确时会抛出异常。
我还Service
使用此method
,但数据始终以在调用期间正确的方式生成。数据由另一个utils类生成。
我知道我应该从Utils
类中抛出此异常 - 但我不能从Service
抛出它 - 所以我必须抓住它。
如何测试,模拟此异常? 对此数据的所有操作都采用私有方法。
我想避免使用PowerMock,因为我听说它是设计糟糕的标志。 所以问题是,如何在良好的设计中实现这一点?
答案 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);