Java-如何测试永远不会发生的异常?
我有Java-如何测试永远不会发生的异常?,java,unit-testing,Java,Unit Testing,我有Utils类和method当给定的数据不正确时抛出异常 我还有服务,它使用这种方法,但数据总是以正确的方式在调用过程中生成的。数据由另一个utils类生成 我知道我应该从Utils类中抛出这个异常,但我不能从服务中抛出它,所以我必须捕获它 我如何测试这个,模拟这个异常? 此数据上的所有操作都在私有方法中 我想避免使用PowerMock,因为我听说这是糟糕设计的标志。 所以问题是,如何在良好的设计中实现这一点?根据您的描述,它看起来是这样的: class Service { publ
Utils
类和method
当给定的数据不正确时抛出异常
我还有服务
,它使用这种方法
,但数据总是以正确的方式在调用过程中生成的。数据由另一个utils类生成
我知道我应该从Utils
类中抛出这个异常,但我不能从服务中抛出它,所以我必须捕获它
我如何测试这个,模拟这个异常?
此数据上的所有操作都在私有方法中
我想避免使用PowerMock,因为我听说这是糟糕设计的标志。
所以问题是,如何在良好的设计中实现这一点?根据您的描述,它看起来是这样的:
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) {
}
}
}
new Service(Utils::aMethod).aMethod(input);
目标如下:
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) {
}
}
}
new Service(Utils::aMethod).aMethod(input);
这种技术被称为
然后,在测试时,您可以简单地为这个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;
}
}
根据您的描述,它如下所示:
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) {
}
}
}
new Service(Utils::aMethod).aMethod(input);
目标如下:
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) {
}
}
}
new Service(Utils::aMethod).aMethod(input);
这种技术被称为
然后,在测试时,您可以简单地为这个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;
}
}
这里有一种方法,您希望引入依赖项注入,但不管出于什么原因,您都不想更改遗留代码
假设您有一些静态实用程序方法,如下所示:
class Utils{
public static Something aMethod(SomethingElse input) throws AnException{
if(input.isValid())
return input.toSomething();
throw new AnException("yadda yadda");
}
}
您有一个使用该实用程序方法的类。您仍然可以向它注入功能接口
@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(x -> { throw new AnException("HA HA"); }).aMethod(input);
这里有一种方法,您希望引入依赖项注入,但不管出于什么原因,您都不想更改遗留代码
假设您有一些静态实用程序方法,如下所示:
class Utils{
public static Something aMethod(SomethingElse input) throws AnException{
if(input.isValid())
return input.toSomething();
throw new AnException("yadda yadda");
}
}
您有一个使用该实用程序方法的类。您仍然可以向它注入功能接口
@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(x -> { throw new AnException("HA HA"); }).aMethod(input);
只是模仿你的Utils类。您应该能够在没有Powermock的情况下对其进行模拟。您可以注入Utils.method,甚至使用FunctionInterface,然后对其进行模拟并使模拟抛出异常您还可以使用反射来访问私有方法(静态方法)。我想我们刚刚发现了您潜在的“指示您的设计不好”。@Line您可以对使用反射实例化对象的持久性框架说同样的话。当存在特殊需求时,反射非常有用。在这种情况下,如果这些方法更适合作为实用方法,那么在测试中使用反射可以更有效地保持生产代码与需求一致。你认为像Mockito这样的框架是如何工作的?只需模拟你的Utils类。您应该能够在没有Powermock的情况下对其进行模拟。您可以注入Utils.method,甚至使用FunctionInterface,然后对其进行模拟并使模拟抛出异常您还可以使用反射来访问私有方法(静态方法)。我想我们刚刚发现了您潜在的“指示您的设计不好”。@Line您可以对使用反射实例化对象的持久性框架说同样的话。当存在特殊需求时,反射非常有用。在这种情况下,如果这些方法更适合作为实用方法,那么在测试中使用反射可以更有效地保持生产代码与需求一致。你认为像Mockito这样的框架是如何工作的?谢谢。但是,如果其他类没有问题的话,那么仅仅测试生产代码不应该使用的几行代码就没有那么多代码了吗/@您不仅获得了可测试性,还获得了可维护性。例如,如果您想在不同的情况下使用不同的数据提供程序,则不必重构服务类。当然,另一种选择是不测试这个分支。实数形式的D实际上是,它指的是通过抽象来解耦模块。依赖项注入倾向于依赖项反转(如本例),但这不是一个要求(例如,注入String
)。依赖项注入没有考虑到SOLID@VinceEmigh谢谢,我把这两个搞混了。我一直记得依赖项反转是控制反转,它不是以D开头的。如果将DataConsumer设置为FunctionInterface,则不需要实现Utils.getConsumer()。可以使用使用Utils.method的lambda,而无需修改该代码。谢谢。但是,如果其他类没有问题的话,那么仅仅测试生产代码不应该使用的几行代码就没有那么多代码了吗/@您不仅获得了可测试性,还获得了可维护性。例如,如果您想在不同的情况下使用不同的数据提供程序,则不必重构服务类。当然,另一种选择是不测试这个分支。实数形式的D实际上是,它指的是通过抽象来解耦模块。依赖项注入倾向于依赖项反转(如本例),但这不是一个要求(例如,注入String
)。依赖项注入没有考虑到SOLID@VinceEmigh谢谢,我把这两个搞混了。我一直记得依赖项反转是控制反转,它不是以D开头的。如果将DataConsumer设置为FunctionInterface,则不需要实现Utils.getConsumer()。可以使用使用Utils.method的lambda,而无需修改该代码。