Here’s another example of using a stub object in Java, but this time for testing a WeatherService
that fetches weather data from an external WeatherAPI
. We’ll use a stub to simulate the behavior of the WeatherAPI
to focus on testing the WeatherService
.
Scenario: Testing a Weather Service
The WeatherService
class depends on the WeatherAPI
to fetch weather information. We want to test the WeatherService
without calling the actual external API.
Implementation
Classes:
// Weather API Interface (Dependency)
public interface WeatherAPI {
String getWeather(String city);
}
// Weather Service (Under Test)
public class WeatherService {
private final WeatherAPI weatherAPI;
public WeatherService(WeatherAPI weatherAPI) {
this.weatherAPI = weatherAPI;
}
public String getWeatherInfo(String city) {
String weather = weatherAPI.getWeather(city);
if ("Sunny".equals(weather)) {
return "It's a sunny day!";
} else if ("Rainy".equals(weather)) {
return "It's a rainy day!";
} else {
return "Weather information unavailable";
}
}
}
Stub for WeatherAPI
:
We create a simple stub for WeatherAPI
that returns hardcoded weather data.
// Stub Implementation for WeatherAPI
public class WeatherAPIStub implements WeatherAPI {
@Override
public String getWeather(String city) {
if ("New York".equals(city)) {
return "Sunny";
} else if ("Seattle".equals(city)) {
return "Rainy";
}
return "Unknown"; // Default case for other cities
}
}
Test Case Using Stub:
Now, let’s write unit tests for WeatherService
using the WeatherAPIStub
to simulate the weather API’s behavior.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class WeatherServiceTest {
@Test
public void testGetWeatherInfoSunny() {
// Arrange: Use the stub
WeatherAPI weatherAPIStub = new WeatherAPIStub();
WeatherService weatherService = new WeatherService(weatherAPIStub);
// Act: Call the method with "New York" which returns "Sunny" from the stub
String weatherInfo = weatherService.getWeatherInfo("New York");
// Assert: Verify the result
assertEquals("It's a sunny day!", weatherInfo);
}
@Test
public void testGetWeatherInfoRainy() {
// Arrange: Use the stub
WeatherAPI weatherAPIStub = new WeatherAPIStub();
WeatherService weatherService = new WeatherService(weatherAPIStub);
// Act: Call the method with "Seattle" which returns "Rainy" from the stub
String weatherInfo = weatherService.getWeatherInfo("Seattle");
// Assert: Verify the result
assertEquals("It's a rainy day!", weatherInfo);
}
@Test
public void testGetWeatherInfoUnknownCity() {
// Arrange: Use the stub
WeatherAPI weatherAPIStub = new WeatherAPIStub();
WeatherService weatherService = new WeatherService(weatherAPIStub);
// Act: Call the method with a city that isn't "New York" or "Seattle"
String weatherInfo = weatherService.getWeatherInfo("Los Angeles");
// Assert: Verify the result for the unknown city
assertEquals("Weather information unavailable", weatherInfo);
}
}
Explanation:
- Stub Implementation (
WeatherAPIStub
):- The stub returns hardcoded responses for specific cities (
"Sunny"
for “New York” and"Rainy"
for “Seattle”). - For any other city, it returns
"Unknown"
.
- The stub returns hardcoded responses for specific cities (
- Tests:
- We write three tests, one for each possible outcome: sunny, rainy, and an unknown city.
- Each test verifies that the
WeatherService
correctly interprets the result from theWeatherAPIStub
.
Benefits of Using Stubs Here:
- Isolation: The test focuses on the
WeatherService
without being dependent on the actualWeatherAPI
. - Controlled Environment: The stub gives predictable outputs, ensuring the unit test is stable and repeatable.
- Testing Edge Cases: You can easily simulate different conditions (e.g., cities with specific weather, unknown cities) without calling a real external service.
This is another classic example of stubbing external dependencies to isolate the unit under test. Would you like to see another example in a different context?