Java 如何使用Mockito消除外部依赖?
我在理解Mockito的概念上有点困难。我已经写了一个小程序来帮助你,但是我不能让它做我想做的事情 这是我的密码:Java 如何使用Mockito消除外部依赖?,java,unit-testing,junit,mocking,mockito,Java,Unit Testing,Junit,Mocking,Mockito,我在理解Mockito的概念上有点困难。我已经写了一个小程序来帮助你,但是我不能让它做我想做的事情 这是我的密码: // WeatherDemo.java: package com.abc; public class WeatherDemo { public String getForecast() { // Get the high remperature for today, and return back to the caller one of these v
// WeatherDemo.java:
package com.abc;
public class WeatherDemo {
public String getForecast() {
// Get the high remperature for today, and return back to the caller one of these values:
// cold, mild, or hot
// cold will be returned if the high temp is forecast to be less than 60.
// hot will be returned if the high temp is forecast to be more than 79.
// Otherwise, mild will be returned (this indicates a high temp in the 60s or 70s).
int highTemp = getHighTemp();
if (highTemp < 60)
return("cold");
if (highTemp > 79)
return("hot");
return("mild");
}
public int getHighTemp() {
// Because this is a demo, we don't have access to any source (web service, DB, etc.) to get the high temp.
// Just hard code a value here, but remember that if this were a real application, we would be dynamically
// retrieving the day's high temperature from some external source.
int highTemp = 32;
return(highTemp);
}
}
================================================================================
// TestWeatherDemo.java:
package com.abc;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.mockito.Mockito;
public class TestWeatherDemo {
@Test
public void testWeatherReport() {
WeatherDemo testMockito = Mockito.mock(WeatherDemo.class);
WeatherDemo testJUnit = new WeatherDemo();
when(testMockito.getHighTemp()).thenReturn(90);
assertEquals("hot", testJUnit.getForecast());
}
}
基本上,我想在getForecast()上运行JUnit。根据一天的高温,它会返回寒冷、温和或炎热。要获取高温,它调用getHighTemp()。让我们假设getHighTemp()调用一个web服务来获取温度(我硬编码了一个值,仅用于测试目的)。因为这是一个外部资源,所以我的Junit没有通过隔离测试,实际上根本不是一个单元测试。更不用说getHighTemp()不会在每次调用时返回相同的值
因此,我想模拟getHighTemp(),告诉它总是返回90的temp
Mockito测试是从testWeatherReport()运行的。这就是我被困的地方。执行此操作时,我可以模拟getHighTemp()返回90:
当(testMockito.getHighTemp())。然后返回(90)
但是,当从getForecast()调用时,我无法使其返回90。断言变得“冷”,因为它选择的是32,而不是90
Mockito背后的全部想法不是我可以模拟一个方法,并告诉它确切返回什么,以移除外部依赖吗?如果从getForecast()调用getHighTemp()不会返回90,我看不出Mockito的用途。我错过了什么?谢谢你的帮助和启发
Bill在您的示例中,
testMockito
和testJUnit
是不同的对象-您在testMockito
上模拟了该方法,但是testJUnit
实际实现了该方法,返回了32
你可能想考虑把代码稍微分解成两个类——然后你可以在
以这种方式构建的代码似乎遵守了规则-例如,如果您需要更改天气数据供应商,这样做的好处是它会让您变得更容易。我认为,当你说“因为这是一个外部资源,我的Junit没有通过隔离测试”时,你无论如何都是沿着这些思路来的。你本质上是在问“如何在测试其余方法的同时模拟类中的一个方法”。可以使用Mockito搜索文档中的“部分mock”。然而,这(几乎)总是表明您的代码结构不好,需要重构。如果您正在测试访问要模拟的接口的类,那么这表明您应该将该接口声明为
接口
,然后将实现传递给该类。这有两个效果:首先,它允许您在不更改接口的情况下更改实现;其次,它使类可测试
因此,在你的情况下:
public interface TempSupplier {
int getHighTemp();
int getLowTemp();
}
public class WeatherDescriber {
private final TempSupplier tempSupplier;
public WeatherDescriber(TempSupplier tempSupplier) {
this.tempSupplier = tempSupplier;
}
public String getForecast() {
int highTemp = tempSupplier.getHighTemp();
...
}
}
@Test
public void testForecast() {
TempSupplier supplier = mock(TempSupplier.class);
when(supplier.getHighTemp()).thenReturn(90);
WeatherDescriber describer = new WeatherDescriber(supplier);
assertThat(describer.getForecast(), is("Hot"));
}
我通常将模拟分为一个单独的方法,以便您可以轻松地进行测试:
private WeatherDescriber getDescriber(int lowTemp, int highTemp) {
TempSupplier supplier = mock(TempSupplier.class);
when(supplier.getLowTemp()).thenReturn(lowTemp);
when(supplier.getHighTemp()).thenReturn(highTemp);
return new WeatherDescriber(supplier);
}
@Test
public void testDescribeVariousTemps() {
assertThat(getDescriber(10, 20).getForecast(), is("cold"));
assertThat(getDescriber(30, 35).getForecast(), is("cold"));
assertThat(getDescriber(40, 45).getForecast(), is("warmer"));
assertThat(getDescriber(90, 130).getForecast(), is("melting"));
}
谢谢@Sprinter。部分模拟对我来说很有效,而且使用非常简单。因为这个例子是为了我自己的Mockito培训,所以我对目前的结果感到满意。接下来,我将重构我的代码,看看还能从中学到什么。谢谢Matt。为了快速修复,我使用了@Sprinter描述的部分模拟。我还将尝试您的建议,并将该类分为两个类,以将模拟方法与真实方法分开。