有一个命令行应用程序可以使用公共API显示明天的天气预报
示例输出如下:
<Tab.Navigator tabBarOptions={tabBarOptions} screenOptions={screenOptions}>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Saved" component={Saved} />
<Tab.Screen name="Map" component={Map} />
<Tab.Screen name="Profile" component={Profile} />
</Tab.Navigator>
问题:如何创建一个测试用例,以使测试不影响真正的服务并在没有Internet的情况下工作。
我尝试创建相同的junit测试,并且在我直接使用api之前可以正常工作。
有人可以帮助我如何为我的单元测试创建一个模拟。
App.java
android:maxLength="200"
ForecastServiceImpl.java
Tomorrow (2019/05/01) in city XYZ:
Clear
Temp: 26.5 °C
Wind: 7.6 mph
Humidity: 61%
ForecastService.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));
}
}
City.java
package api;
import model.City;
import model.Forecast;
import retrofit2.Call;
import retrofit2.Retrofit;
import util.PathDate;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
public class ForecastServiceImpl {
private Retrofit retrofit;
public ForecastServiceImpl(Retrofit retrofit) {
this.retrofit = retrofit;
}
public String getForecast(String cityName, LocalDate date) throws IOException {
PathDate pathDate = new PathDate(date);
ForecastService service = retrofit.create(ForecastService.class);
Call<List<City>> findCityCall = service.findCityByName(cityName.toLowerCase());
City city = Objects.requireNonNull(findCityCall.execute().body())
.stream()
.findFirst()
.orElseThrow(() -> new RuntimeException(String.format("Can't find city id for %s", cityName)));
Call<List<Forecast>> forecastCall = service.getForecast(city.getWoeid(), pathDate);
Forecast forecast = Objects.requireNonNull(forecastCall.execute().body())
.stream()
.findFirst()
.orElseThrow(() -> new RuntimeException(String.format("Can't get forecast for %s", cityName)));
return String.format("Weather on (%s) in %s:\n%s", pathDate, city.getTitle(), forecast);
}
}
Forecast.java
package api;
import model.City;
import model.Forecast;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
import util.PathDate;
import java.util.List;
public interface ForecastService {
@GET("/api/location/{city_id}/{date}/")
Call<List<Forecast>> getForecast(@Path("city_id") Long cityId, @Path("date") PathDate date);
@GET("/api/location/search/")
Call<List<City>> findCityByName(@Query("query") String city);
}
PathDate.java
package model;
public class City {
private String title;
private Long woeid;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Long getWoeid() {
return woeid;
}
public void setWoeid(Long woeid) {
this.woeid = woeid;
}
}
Utils.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);
}
}
答案 0 :(得分:0)
service.getForecast(city.getWoeid(), pathDate);
返回一个Call<List<Forecast>>
对象。当我们在该对象上调用execute
时,将进行实际的API调用。由于我们不想执行实际的API调用,因此可以尝试模拟Call<List<Forecast>>
对象。
我们可以像模拟Call
类
Call<List<Forecast>> mockedListForeCast = mock(Call.class);
以上语句创建Call<List<Forecast>>
的模拟对象。我们可以使用when
来定义在模拟对象上调用方法时应该发生的情况。
// here I am returning the singleton list, you can return a list of forecast
when(mockedListForeCast.execute()).thenReturn(Response.success(Collections.singletonList()));
上一行说,当在模拟对象上调用execute函数时,返回一个空的预测列表。
通过这种方式,我们可以模拟API响应,而不必进行实际的API调用。
修改:
您还可以使用Retrofit Mock来模拟改造API。