Lab 13a: Setup for External API Call
Description¶
To support different currencies for the coffee order's total price, we'll call to an external currency conversion API using Spring's RestTemplate
. This will be done in two steps:
-
Create a stub service that returns a hard-coded value, to make sure it's integrated properly.
-
A real,
RestTemplate
-based implementation that calls out to the API service.
Goals¶
- Learn about using a Stub (type of Test Double) to ease development
- Understand the basics of
RestTemplate
- See how
@Profile
can be used to switch among services used - Learn about handling HTTP query parameters with
@RequestParam
a. Stub Service¶
You'll create a Stub version of the currency conversion service. Remember that a stub returns a hard-coded value.
-
Create a new interface named
CurrencyConversionService
in the domain package with the following method:int convertToBritishPound(int amount);
Note that this is a domain service, so has no annotations and is in the
.domain
package. -
Create a new package,
com.welltestedlearning.coffeekiosk.adapter.out.currency
and in that package, create a class namedStubCurrencyConversionService
that implementsCurrencyConversionService
and returns a hard-coded value of1234
. -
As this is an adapter service, add Spring's
@Service
annotation to the stub implementation.
b. Add Query Parameter¶
To support optionally returning the price in a different currency, we'll add support for a query parameter.
Open the CoffeeOrderController
class and make the following changes:
-
Use auto-wiring to inject the
CurrencyConversionService
interface as an additional parameter of the existing constructor and save it as a final instance field. -
Change the
coffeeOrder()
method to accept an optional Query Parameter by changing the method signature to:public ResponseEntity<CoffeeOrderResponse> coffeeOrder( @PathVariable("id") long orderId, @RequestParam(value = "currency", defaultValue = "usd") String currency) {
The
@RequestParam
annotation will cause Spring to assign an incoming Query Parameter (e.g.,?currency=gbp
) to thecurrency
variable.The
defaultValue="usd"
means that if no query parameter is sent,currency
will default to"usd"
.
c. Failing Test¶
Not all tests with Spring controllers have to use Spring support, we can write a unit test that instantiates the controller directly and test it as if Spring weren't involved.
-
Create a new (or add to the existing)
CoffeeOrderControllerTest
class and add the following test:@Test public void getWithPoundQueryParamConvertsPriceToPounds() throws Exception { // Given: an order is in the repository CoffeeOrderRepository coffeeOrderRepository = new InMemoryCoffeeOrderRepository(); // empty coffee order is fine as the price will be ignored for this test CoffeeOrder coffeeOrder = new CoffeeOrder("Spring", LocalDateTime.now()); coffeeOrder.setId(12L); // need to have an id so we know we get an existing order coffeeOrderRepository.save(coffeeOrder); CoffeeOrderController controller = new CoffeeOrderController(coffeeOrderRepository, new StubCurrencyConversionService()); // When: we do a GET with "gbp" currency CoffeeOrderResponse response = controller.coffeeOrder(coffeeOrder.getId(), "gbp").getBody(); // Then: price will always be 1234, because that's what the Stub says assertThat(response.getTotalPrice()) .isEqualTo("1234"); }
-
This test should fail, but all other tests should still pass.
d. Use the Service¶
In the CoffeeOrderController
class, implement the following behavior in the coffeeInfo
method:
-
If
currency
parameter is"gbp"
, then:-
Take the
totalPrice
from the coffee order (after it's retrieved from the repository) -
Use the
CurrencyConversionService
to convert the price into Pounds -
Store the converted price into the
CoffeeOrderResponse
as the price.
-
If implemented properly, the tests should pass.
e. Try It Out¶
Try it out using the browser, curl
, or Postman, both with and without the query parameter of ?currency=gbp
:
f. Increase Prices¶
To make the examples work better (since we're working with whole numbers), change the pricing in the CoffeeItem.price()
method so that it's multiples of 100. Instead of 1, 2, and 3, change to 100, 200, and 300.