Lab06 get order api
In this lab you'll create a GET mapping to support retrieving orders that are in the repository.
A. Create Failing Integration Test¶
You'll start with a couple of failing integration tests.
Copy this into a new Test class called MealOrderApiIntegrationTest
:
package com.welltestedlearning.mealkiosk.api;
import com.welltestedlearning.mealkiosk.adapter.MealBuilder;
import com.welltestedlearning.mealkiosk.domain.MealOrder;
import com.welltestedlearning.mealkiosk.domain.MealOrderRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MealOrderApiIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private MealOrderRepository mealOrderRepository;
@Test
public void postCreatesNewMealOrder() throws Exception {
mockMvc.perform(post("/api/mealorder")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"burger\": \"cheese\", \"drinkSize\": \"large\", \"friesSize\": \"regular\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value("0"));
}
@Test
public void getWithIdReturnsPriceAndId() throws Exception {
MealOrder mealOrder = MealBuilder.builder().burger("cheese").build();
mealOrder.setId(1L); // force id to be 1 for this test
mealOrderRepository.save(mealOrder);
mockMvc.perform(get("/api/mealorder/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.price").value("6"));
}
}
Try running this test class: the first test (testing the POST mapping) will pass, but the second test should fail, because you haven't yet written code for the GET mapping. We'll do that next.
B. Create Failing Unit Test¶
Before we write the code for the GET mapping, let's have a failing Unit Test.
- Open up
MealOrderApiControllerTest
-
Add this test:
@Test public void getReturnsExistingMealOrderById() throws Exception { // Given: a repository with a meal order saved MealOrderRepository mealOrderRepository = new MealOrderRepository(); MealOrder mealOrder = MealBuilder.builder().burger("cheese").build(); MealOrder savedMealOrder = mealOrderRepository.save(mealOrder); // And: an API Controller with the repository MealOrderApiController controller = new MealOrderApiController(mealOrderRepository); // When: we do a GET for the ID Long savedMealOrderId = savedMealOrder.getId(); MealOrderResponse response = controller.findMealOrder(savedMealOrderId); // Then: we expect the response to have the same ID and price assertThat(response.getId()) .isEqualTo(savedMealOrderId.toString()); assertThat(response.getPrice()) .isEqualTo(mealOrder.price()); }
Now add add the minimum implementation to make the test compile
- Open up the
MealOrderApiController
class - Add the following method:
@GetMapping("/api/mealorder/{id}") public MealOrderResponse findMealOrder(@PathVariable("id") Long id) { return null; }
- Run this test: it should fail (with a
NullPointerException
)
C. Make Test Pass: GET Mapping Method¶
Run All Tests
If you run all of the tests, you should have two failing tests: the failing Unit Test and the failing Integration Test. If you have more than two failing tests, fix them before moving on.
Now that you have failing tests, write the code to make the tests pass.
In the MealOrderApiController
class, inside of the findMealOrder
method, fill in the implementation steps:
- Lookup the meal order in the repository by the ID that was passed in
- Transform the
MealOrder
to aMealOrderResponse
- Return the response object
- Run all tests
D. Handle Non-Existent ID¶
What if the requested ID isn't found? We'd like to get a 404 (Not Found) status code back. Let's add an Integration Test that checks for that.
- Open
MealOrderApiIntegrationTest
and add the following test@Test public void getWithNonExistentIdReturnsNotFound() throws Exception { mockMvc.perform(get("/api/mealorder/999")) .andExpect(status().isNotFound()); }
-
Run it and it should fail.
How Did It Fail?
What was the root cause of the failure, i.e., which Exception? It's a good idea to get used to looking at the stack trace for the failure reason.
In order to return a Not Found status code, you will throw
a special exception.
-
Create a new exception class (in the
api
package) calledNoSuchOrderException
:import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "No order with that ID was found.") public class NoSuchOrderException extends RuntimeException { }
-
Open up the
MealOrderApiController
and add code inside thefindMealOrder
method that throws the exception if the order wasn't found.Throwing Exception
To throw the exception, you can do:
throw new NoSuchOrderException()
-
Run the tests, they should all pass.
-
Also try out the API using Postman/curl and the browser.