Java 为单元测试创建模拟-改装

Java 为单元测试创建模拟-改装,java,api,mocking,retrofit,Java,Api,Mocking,Retrofit,有一款命令行应用程序可以使用公共API显示明天的天气预报 示例输出可以如下所示: Tomorrow (2019/05/01) in city XYZ: Clear Temp: 26.5 °C Wind: 7.6 mph Humidity: 61% 问题:您将如何创建一个测试用例,使测试在没有互联网的情况下不会接触到真正的服务和工作 我试着为它创建junit测试,直到我直接使用api为止,它工作得很好 有人可以帮助我如何为我的单元测试创建一个模拟 App.java import api.Fore

有一款命令行应用程序可以使用公共API显示明天的天气预报

示例输出可以如下所示:

Tomorrow (2019/05/01) in city XYZ:
Clear
Temp: 26.5 °C
Wind: 7.6 mph
Humidity: 61%
问题:您将如何创建一个测试用例,使测试在没有互联网的情况下不会接触到真正的服务和工作

我试着为它创建junit测试,直到我直接使用api为止,它工作得很好

有人可以帮助我如何为我的单元测试创建一个模拟

App.java

import api.ForecastServiceImpl;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import java.io.IOException;
import java.time.LocalDate;

public class App {

    public static void main(String[] args) throws IOException {
        if (args.length < 1) {
            System.out.println("Pass city name as an argument");
            System.exit(1);
        }

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://www.metaweather.com")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        LocalDate tomorrow = LocalDate.now().plusDays(1);

        ForecastServiceImpl service = new ForecastServiceImpl(retrofit);
        System.out.println(service.getForecast(args[0], tomorrow));
    }
}
Forecast.java

package model;

import com.google.gson.annotations.SerializedName;

public class Forecast {

    private Long id;
    @SerializedName("weather_state_name")
    private String weatherState;
    @SerializedName("wind_speed")
    private Double windSpeed;
    @SerializedName("the_temp")
    private Double temperature;
    private Integer humidity;

    public Long getId() {
        return id;
    }

    public Forecast setId(Long id) {
        this.id = id;
        return this;
    }

    public String getWeatherState() {
        return weatherState;
    }

    public Forecast setWeatherState(String weatherState) {
        this.weatherState = weatherState;
        return this;
    }

    public Double getWindSpeed() {
        return windSpeed;
    }

    public Forecast setWindSpeed(Double windSpeed) {
        this.windSpeed = windSpeed;
        return this;
    }

    public Double getTemperature() {
        return temperature;
    }

    public Forecast setTemperature(Double temperature) {
        this.temperature = temperature;
        return this;
    }

    public Integer getHumidity() {
        return humidity;
    }

    public Forecast setHumidity(Integer humidity) {
        this.humidity = humidity;
        return this;
    }

    @Override
    public String toString() {
        return String.format("%s\nTemp: %.1f °C\nWind: %.1f mph\nHumidity: %d%%",
                weatherState, temperature, windSpeed, humidity);
    }
}

PathDate.java

package util;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class PathDate {

    private final LocalDate date;

    public PathDate(LocalDate date) {
        this.date = date;
    }

    @Override public String toString() {
        return date.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
    }
}
Utils.java

package util;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Utils {

    public static byte[] readResourceFileToBytes(String filename) {
        byte[] fileBytes = new byte[0];
        try {
            Path path = Paths.get(Utils.class.getClassLoader().getResource(filename).toURI());
            fileBytes = Files.readAllBytes(path);
        } catch (URISyntaxException|IOException|NullPointerException e) {
            e.printStackTrace();
        }

        return fileBytes;
    }
}


service.getForecast(city.getWoeid(),pathDate)
返回给我们一个
调用
对象。当我们在这个对象上调用
execute
时,就会进行实际的API调用。因为我们不想执行实际的API调用,所以可以尝试模拟
调用
对象

我们可以模拟
调用

Call<List<Forecast>> mockedListForeCast = mock(Call.class);

上面的一行表示,当对模拟对象调用execute函数时,返回一个空的forecast列表

// here I am returning the singleton list, you can return a list of forecast
when(mockedListForeCast.execute()).thenReturn(Response.success(Collections.singletonList()));

这样我们就模拟了API响应,而不必进行实际的API调用

编辑


您还可以使用
服务.getForecast(city.getWoeid(),pathDate)模拟改造API
返回给我们一个
调用
对象。当我们在这个对象上调用
execute
时,就会进行实际的API调用。因为我们不想执行实际的API调用,所以可以尝试模拟
调用
对象

我们可以模拟
调用

Call<List<Forecast>> mockedListForeCast = mock(Call.class);

上面的一行表示,当对模拟对象调用execute函数时,返回一个空的forecast列表

// here I am returning the singleton list, you can return a list of forecast
when(mockedListForeCast.execute()).thenReturn(Response.success(Collections.singletonList()));

这样我们就模拟了API响应,而不必进行实际的API调用

编辑


您还可以使用模拟改装API。

您想为
getForecast
函数编写测试,对吗?是,对吗getForecast@DeepakPatankar你能帮我做一个示例测试用例吗?你想为
getForecast
函数编写测试吗?是正确的getForecast@DeepakPatankar你能帮我吗帮我看一个测试用例示例?嘿@user1873274,这就是我在代码中编写单元测试的方式。你能检查一下这是否也适用于你吗?您可能需要更改上述答案中的某些行以适应您的情况。谢谢@Deepak,但有一个问题,请解释一下,因为我是新来的。我是否应该创建一个方法mock,在其中我应该使用call类进行模拟,然后定义模拟输出?当然,将添加explanationHey@user1873274,我已经更新了我的答案。请让我知道它是否有帮助。我理解这个概念,它很有帮助。我需要在测试类本身中创建mock方法吗?还是应该单独上课?抱歉问了太多基本的问题。嘿@user1873274,这就是我在代码中编写单元测试的方式。你能检查一下这是否也适用于你吗?您可能需要更改上述答案中的某些行以适应您的情况。谢谢@Deepak,但有一个问题,请解释一下,因为我是新来的。我是否应该创建一个方法mock,在其中我应该使用call类进行模拟,然后定义模拟输出?当然,将添加explanationHey@user1873274,我已经更新了我的答案。请让我知道它是否有帮助。我理解这个概念,它很有帮助。我需要在测试类本身中创建mock方法吗?还是应该单独上课?抱歉问了太基本的问题。
// here I am returning the singleton list, you can return a list of forecast
when(mockedListForeCast.execute()).thenReturn(Response.success(Collections.singletonList()));