// [...]/controllers/HelloWorldController.java
@RestController
public class HelloWorldController {
@GetMapping("/hello-world")
public String helloWorld() {
return "Hello World";
}
}
// [...]/test/[...]/controllers/HelloWorldControllerTest.java
@RunWith(SpringRunner.class)
@WebMvcTest(HelloWorldController.class)
public class HelloWorldControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void helloWorld_basic() throws Exception {
RequestBuilder request = MockMvcRequestBuilders
.get("/hello-world")
.accept(MediaType.APPLICATION_JSON);
// call "/hello-world" GET application/json
MockMvc result = mockMvc.perform(request).andReturn();
// verify "Hello World"
assertEquals("Hello World", result.getResponse().getContentAsString());
}
}
// [...]/test/[...]/controllers/HelloWorldControllerTest.java
@RunWith(SpringRunner.class)
@WebMvcTest(HelloWorldController.class)
public class HelloWorldControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void helloWorld_basic() throws Exception {
RequestBuilder request = MockMvcRequestBuilders
.get("/hello-world")
.accept(MediaType.APPLICATION_JSON);
// call "/hello-world" GET application/json
MockMvc result = mockMvc.perform(request)
.andExpect(status().isOk()) // <-- check status is ok
.andExpect(content().string("Hello World")) // <-- check content
.andReturn();
// verify "Hello World"
// assertEquals("Hello World", result.getResponse().getContentAsString()); // <-- no longer needed
}
}
// [...]/models/Item.java
public class Item {
private int id;
private String name;
private int price;
private int quantity;
public Item(int id, String name, int price, int quantity) {
this.id = id;
this.name = name;
this.price = price;
this.quantity = quantity;
}
// ...getters and setters
public String toString() {
return String.format("Item[%d, %s, %d, %d]", id, name, price, quantity);
}
}
// [...]/controllers/ItemController.java
@RestController
public class ItemController {
@GetMapping("/dummy-item")
public Item dummyItem() {
return new Item(1, "Ball", 10, 100);
}
}
// [...]/test/[...]/models/ItemControllerTest.java
@RunWith(SpringRunner.class)
@WebMvcTest(ItemController.class)
public class HelloWorldControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void dummyItem_basic() throws Exception {
RequestBuilder request = MockMvcRequestBuilders
.get("/dummy-item")
.accept(MediaType.APPLICATION_JSON);
// call "/dummy-item" GET application/json
MockMvc result = mockMvc.perform(request)
.andExpect(status().isOk()) // <-- check status is ok
.andExpect(content().json(
"{\"id\":1,\"name\":\"ball\",\"price\":10,\"quantity\":100}" // <-- check content
))
.andReturn();
}
}
JSONAssert.assertEquals(expected, actual, isStrict);
// [...]/test/[...]/spike/JsonAssertTest.java
public class JsonAssertTest {
String actual = "{\"id\":1,\"name\":\"ball\",\"price\":10,\"quantity\":100}";
@Test
public void jsonAssert_StrictTrue_ExactMatchExceptForSpaces() throws JSONException {
String expected = "{\"id\":1,\"name\":\"ball\",\"price\":10,\"quantity\":100}";
JSONAssert.assert(expected, actual, true);
}
@Test
public void jsonAssert_StrictFalse() throws JSONException {
String expected = "{\"id\":1,\"name\":\"ball\",\"price\":10,\"quantity\":100}";
JSONAssert.assert(expected, actual, false);
}
@Test
public void jsonAssert_WithoutEscapeCharacters() throws JSONException {
String expected = "{id:1,name:ball,price:10,quantity:100}";
JSONAssert.assert(expected, actual, false);
}
}
// [...]/services/BusinessService.java
@Service
public class ItemBusinessService {
public Item retrieveHardcodedItem() {
return new Item(1, "Ball", 10, 100);
}
}
// [...]/controllers/ItemController.java
@RestController
public class ItemController {
@Autowired
private ItemBusinessService businessService;
// ...
@GetMapping("/item-from-business-service")
public Item itemFromBusinessService() {
return businessService.retrieveHardcodedItem();
}
}
// [...]/test/[...]/models/ItemControllerTest.java
@RunWith(SpringRunner.class)
@WebMvcTest(ItemController.class)
public class HelloWorldControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ItemBusinessService businessService;
@Test
public void dummyItem_basic() throws Exception {
// ...
}
@Test
public void itemFromBusinessService() {
when(businessService.retrieveHardcodedItem()).thenReturn(
new Item(2, "Item 2", 10, 10)
);
RequestBuilder request = MockMvcRequestBuilders
.get("/item-from-business-service")
.accept(MediaType.APPLICATION_JSON)
MockMvc result = mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().json("{id:2,name:Item 2,price:10,quantity:10}"))
.andReturn();
}
}
// pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
// [...]/models/Item.java
@Entity
public class Item {
@Id
private int id;
private String name;
private int price;
private int quantity;
@Transient
private int value;
public Item() {}
public Item(int id, String name, int price, int quantity) {
this.id = id;
this.name = name;
this.price = price;
this.quantity = quantity;
}
// ...getters and setters
public String toString() {
return String.format("Item[%d, %s, %d, %d]", id, name, price, quantity);
}
}
# application.properties
spring.jpa.show-sql=true
# jdbc url: jdbc:h2:mem:testdb
spring.h2.console.enabled=true
-- data.sql
insert into item (id, name, price, quantity) values (10001, 'item1', 10, 20);
insert into item (id, name, price, quantity) values (10002, 'item2', 5, 10);
insert into item (id, name, price, quantity) values (10003, 'item3', 15, 2);
// [...]/repositories/ItemRepository.java
public interface ItemRepository extends JpaRepository<Item, Integer> {
}
// [...]/services/BusinessService.java
@Service
public class ItemBusinessService {
@Autowired
private ItemRepository repository;
public Item retrieveHardcodedItem() {
// ...
}
public List<Item> retrieveAllItems() {
List<Item> items = repository.findAll();
for (Item item : items) {
item.setValue(item.getPrice() * item.getQuantity());
}
return items;
}
}
// [...]/controllers/ItemController.java
@RestController
public class ItemController {
@Autowired
private ItemBusinessService businessService;
// ...
@GetMapping("/all-items-from-database")
public List<Item> retrieveAllItems() {
return businessService.retrieveAllItems();
}
}
// [...]/test/[...]/models/ItemControllerTest.java
@RunWith(SpringRunner.class)
@WebMvcTest(ItemController.class)
public class HelloWorldControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ItemBusinessService businessService;
// ...
@Test
public void retrieveAllItems_basic() throws Exception {
when(businessService.retrieveAllItems()).thenReturn(
Arrays.asList(
new Item(2, "Item2", 10, 10),
new Item(3, "Item3", 20, 20)
);
);
RequestBuilder request = MockMvcRequestBuilders
.get("/all-items-from-database")
.accept(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(request)
.andExpect(status().ok())
.andExpect(content().json("[{id:2,name:Item2,price:10},{id:3,name:Item3,price:20}]"))
.andReturn();
}
}
// [...]/test/[...]/services/ItemBusinessServiceTest.java
@RunWith(MockitoJUnitRunner.class)
public class ItemBusinessServiceTest {
@InjectMocks
private ItemBusinessService business;
@Mock
private ItemRepository repository;
@Test
public void retrieveAllItems() {
when(repository.findAll()).thenReturn(
Arrays.asList(
new Item(2, "Item2", 10, 10),
new Item(3, "Item3", 20, 20)
)
);
List<Item> items = business.retrieveAllItems();
assertEquals(100, items.get(0).getValue());
assertEquals(400, items.get(1).getValue());
}
}
// [...]/test/[...]/repositories/ItemRepositoryTest.java
// depends on what lives inside the data.sql file
// generally you would only write these tests if you want to add your own custom repository methods
// debate-able because it you are still technically testing spring framework
// you can also define your data.sql file in the src/test/resources folder to override the test data
@RunWith(SpringRunner.class)
@DataJpaTest
public class ItemRepositoryTest {
@Autowired
private ItemRepository repository;
@Test
public void testFindAll() {
List<Item> items = repository.findAll();
assertEquals(3, items.size());
}
}
// [...]/test/[...]/controllers/ItemControllerIT.java
// IT: Integration Test
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ItemControllerIT {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void contextLoads() throws JSONException {
String response = this.restTemplate.getForObject("/all-items-from-database", String.class);
JSONAssert.assertEquals("[{id:10001},{id:10002},{id:10003}]", response, false);
}
}
@MockBean
private ItemRepository repository;
@Test
public void contextLoads() {
when(...).thenReturn(...);
}
# src/test/resources/application.properties
# this configuration will override what resides in src/main/resources/application.properties
spring.jpa.show-sql=false
spring.h2.console.enabled=false
Or...
// above the test class
@TestPropertySource(locations = {"classpath:test-configuration.properties"})
# src/test/resources/test-configuration.properties
# ...
// post request
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/items")
.accept(MediaType.APPLICATION_JSON)
.content("{id:1,name:Ball,price:10,quantity:100}")
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder)
.andExpect(status().isCreated())
.andExpect(header().string("location", containsString("/item/")))
.andReturn();
// procedural
public int calculateSum(int[] data) {
int sum = 0;
for (int value : data) {
sum += value;
}
return sum;
}
// functional
public int calculateSum(int... data) {
return Arrays.stream(data).reduce(Integer::sum).orElse(0);
}
// [...]/test/[...]/spike/HamcrestMatchersTest.java
public class HamcrestMatchersTest {
@Test
public void learning() {
List<Integer> numbers = Arrays.asList(12, 15, 45);
assertThat(numbers, hasSize(3));
assertThat(numbers, hasItems(12,45));
assertThat(numbers, everyItem(greaterThan(10)));
assertThat(numbers, everyItem(lessThan(100)));
assertThat("", isEmptyString());
assertThat("ABCDE", containsString("BCD"));
assertThat("ABCDE", startsWith("ABC"));
assertThat("ABCDE", endsWith("CDE"));
}
}
// [...]/test/[...]/spike/AssertJTest.java
public class AssertJTest {
@Test
public void learning() {
List<Integer> numbers = Arrays.asList(12,15,45);
assertThat(numbers)
.hasSize(3)
.contains(12,15)
.allMatch(x -> x > 10)
.allMatch(x -> x < 100)
.noneMatch(x -> x < 0);
assertThat("").isEmpty();
assertThat("ABCDE")
.contains("BCD")
.startsWith("ABC")
.endsWith("CDE");
}
}
// [...]/test/[...]/spike/JsonPathTest.java
public class JsonPathTest {
@Test
public void learning() {
String responseFromService = "[" +
"{\"id\": 10000, \"name\": \"Pencil\", \"quantity\": 5}," +
"{\"id\": 10001, \"name\": \"Pen\", \"quantity\": 15}," +
"{\"id\": 10002, \"name\": \"Eraser\", \"quantity\": 10}" +
"]";
DocumentContext context = JsonPath.parse(responseFromService);
int length = context.read("$.length");
assertThat(length).isEqualTo(3);
List<Integer> ids = context.read("$..id").toString();
assertThat(ids).containsExactly(10000,10001,10002);
context.read("$.[1]").toString();
context.read("$.[0:2]").toString();
context.read("$.[?(@.name=='Eraser')]").toString();
context.read("$.[?(@.quantity==5)]").toString();
}
}