diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/Employee.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/Employee.java new file mode 100644 index 0000000000..a805e8f5fe --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/Employee.java @@ -0,0 +1,43 @@ +package org.baeldung.boot.boottest; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "person") +public class Employee { + + public Employee() { + } + + public Employee(String name) { + this.name = name; + } + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Size(min = 3, max = 20) + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRepository.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRepository.java new file mode 100644 index 0000000000..fa234f0e3a --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRepository.java @@ -0,0 +1,21 @@ +package org.baeldung.boot.boottest; + +import java.util.List; +import java.util.Optional; + +import javax.transaction.Transactional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +@Transactional +public interface EmployeeRepository extends JpaRepository { + + public Optional findByName(String name); + + public Optional findById(Long id); + + public List findAll(); + +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRestController.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRestController.java new file mode 100644 index 0000000000..8442fc03a3 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeRestController.java @@ -0,0 +1,32 @@ +package org.baeldung.boot.boottest; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +public class EmployeeRestController { + + @Autowired + EmployeeService employeeService; + + @RequestMapping(value = "/employees", method = RequestMethod.POST) + public ResponseEntity createEmployee(@RequestBody Employee employee) { + HttpStatus status = HttpStatus.CREATED; + Employee saved = employeeService.save(employee); + return new ResponseEntity<>(saved, status); + } + + @RequestMapping(value = "/employees", method = RequestMethod.GET) + public List getAllEmployees() { + return employeeService.getAllEmployees(); + } + +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeService.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeService.java new file mode 100644 index 0000000000..f0ed49e699 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeService.java @@ -0,0 +1,17 @@ +package org.baeldung.boot.boottest; + +import java.util.List; +import java.util.Optional; + +public interface EmployeeService { + + public Optional getEmployeeById(Long id); + + public Optional getEmployeeByName(String name); + + public List getAllEmployees(); + + public boolean exists(String email); + + public Employee save(Employee employee); +} diff --git a/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeServiceImpl.java b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeServiceImpl.java new file mode 100644 index 0000000000..21936255e0 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/boot/boottest/EmployeeServiceImpl.java @@ -0,0 +1,45 @@ +package org.baeldung.boot.boottest; + +import java.util.List; +import java.util.Optional; + +import javax.transaction.Transactional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Transactional +public class EmployeeServiceImpl implements EmployeeService { + + @Autowired + EmployeeRepository employeeRepository; + + @Override + public Optional getEmployeeById(Long id) { + return employeeRepository.findById(id); + } + + @Override + public Optional getEmployeeByName(String name) { + return employeeRepository.findByName(name); + } + + @Override + public boolean exists(String name) { + if (employeeRepository.findByName(name) != null) { + return true; + } + return false; + } + + @Override + public Employee save(Employee employee) { + return employeeRepository.save(employee); + } + + @Override + public List getAllEmployees() { + return employeeRepository.findAll(); + } +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRepositoryTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRepositoryTest.java new file mode 100644 index 0000000000..f47e28a7e1 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRepositoryTest.java @@ -0,0 +1,78 @@ +package org.baeldung.boot.boottest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; + +import org.baeldung.boot.boottest.Employee; +import org.baeldung.boot.boottest.EmployeeRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@DataJpaTest +public class EmployeeRepositoryTest { + + @Autowired + TestEntityManager entityManager; + + @Autowired + EmployeeRepository employeeRepository; + + @Test + public void whenFindByName_thenReturnEmployee() { + Employee emp = new Employee("test"); + entityManager.persistAndFlush(emp); + + Optional fromDb = employeeRepository.findByName(emp.getName()); + assertThat(fromDb.get() + .getName()).isEqualTo(emp.getName()); + } + + @Test(expected = NoSuchElementException.class) + public void whenInvalidName_thenNoSuchElementException() { + Optional fromDb = employeeRepository.findByName("doesNotExist"); + fromDb.get(); + } + + @Test + public void whenFindById_thenReturnEmployee() { + Employee emp = new Employee("test"); + entityManager.persistAndFlush(emp); + + Optional fromDb = employeeRepository.findById(emp.getId()); + assertThat(fromDb.get() + .getName()).isEqualTo(emp.getName()); + } + + @Test(expected = NoSuchElementException.class) + public void whenInvalidId_thenNoSuchElementException() { + Optional fromDb = employeeRepository.findById(-11L); + fromDb.get(); + } + + @Test + public void givenSetOfEmployees_whenFindAll_thenReturnAllEmployees() { + Employee alex = new Employee("alex"); + Employee ron = new Employee("ron"); + Employee bob = new Employee("bob"); + + entityManager.persist(alex); + entityManager.persist(bob); + entityManager.persist(ron); + entityManager.flush(); + + List allEmployees = employeeRepository.findAll(); + + assertThat(allEmployees).hasSize(3) + .extracting(Employee::getName) + .containsOnly(alex.getName(), ron.getName(), bob.getName()); + } + +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerIntTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerIntTest.java new file mode 100644 index 0000000000..6360970345 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerIntTest.java @@ -0,0 +1,78 @@ +package org.baeldung.boot.boottest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasSize; +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.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.IOException; +import java.util.List; + +import org.baeldung.boot.DemoApplication; +import org.junit.After; +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.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.MediaType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = DemoApplication.class) +@AutoConfigureMockMvc +@TestPropertySource(locations = "classpath:application-integrationtest.properties") +public class EmployeeRestControllerIntTest { + + @Autowired + MockMvc mvc; + + @Autowired + EmployeeRepository repository; + + @After + public void resetDb() { + repository.deleteAll(); + } + + @Test + public void whenValidInput_thenCreateEmployee() throws IOException, Exception { + Employee bob = new Employee("bob"); + mvc.perform(post("/api/employees").contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.toJson(bob))); + + List found = repository.findAll(); + assertThat(found).extracting(Employee::getName) + .containsOnly("bob"); + } + + @Test + public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception { + + createTestEmployee("bob"); + createTestEmployee("alex"); + + mvc.perform(get("/api/employees").contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(greaterThanOrEqualTo(2)))) + .andExpect(jsonPath("$[0].name", is("bob"))) + .andExpect(jsonPath("$[1].name", is("alex"))); + } + + private void createTestEmployee(String name) { + Employee emp = new Employee(name); + repository.saveAndFlush(emp); + } + +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerTest.java new file mode 100644 index 0000000000..0187ba9969 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeRestControllerTest.java @@ -0,0 +1,78 @@ +package org.baeldung.boot.boottest; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +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; + +import java.util.Arrays; +import java.util.List; + +import org.baeldung.boot.boottest.Employee; +import org.baeldung.boot.boottest.EmployeeRestController; +import org.baeldung.boot.boottest.EmployeeService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.internal.verification.VerificationModeFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +@RunWith(SpringRunner.class) +@WebMvcTest(EmployeeRestController.class) +public class EmployeeRestControllerTest { + + @Autowired + MockMvc mvc; + + @MockBean + EmployeeService service; + + @Before + public void setUp() throws Exception { + } + + @Test + public void whenPostEmployee_thenCreateEmployee() throws Exception { + Employee alex = new Employee("alex"); + given(service.save(Mockito.anyObject())).willReturn(alex); + + mvc.perform(post("/api/employees").contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.toJson(alex))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.name", is("alex"))); + verify(service, VerificationModeFactory.times(1)).save(Mockito.anyObject()); + reset(service); + } + + @Test + public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception { + Employee alex = new Employee("alex"); + Employee john = new Employee("john"); + Employee bob = new Employee("bob"); + + List allEmployees = Arrays.asList(alex, john, bob); + + given(service.getAllEmployees()).willReturn(allEmployees); + + mvc.perform(get("/api/employees").contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(3))) + .andExpect(jsonPath("$[0].name", is(alex.getName()))) + .andExpect(jsonPath("$[1].name", is(john.getName()))) + .andExpect(jsonPath("$[2].name", is(bob.getName()))); + verify(service, VerificationModeFactory.times(1)).getAllEmployees(); + reset(service); + } + +} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeServiceImplTest.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeServiceImplTest.java new file mode 100644 index 0000000000..345cc29e02 --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/EmployeeServiceImplTest.java @@ -0,0 +1,145 @@ +package org.baeldung.boot.boottest; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; + +import org.baeldung.boot.boottest.Employee; +import org.baeldung.boot.boottest.EmployeeRepository; +import org.baeldung.boot.boottest.EmployeeService; +import org.baeldung.boot.boottest.EmployeeServiceImpl; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.internal.verification.VerificationModeFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +public class EmployeeServiceImplTest { + + @TestConfiguration + static class EmployeeServiceImplTestContextConfiguration { + @Bean + public EmployeeService employeeService() { + return new EmployeeServiceImpl(); + } + } + + @Autowired + EmployeeService employeeService; + + @MockBean + EmployeeRepository employeeRepository; + + @Before + public void setUp() { + Employee john = new Employee("john"); + john.setId(11L); + + Optional emp = Optional.of(john); + + Employee bob = new Employee("bob"); + Employee alex = new Employee("alex"); + + List allEmployees = Arrays.asList(john, bob, alex); + + Mockito.when(employeeRepository.findByName(john.getName())) + .thenReturn(emp); + Mockito.when(employeeRepository.findByName("wrong_name")) + .thenReturn(Optional.empty()); + Mockito.when(employeeRepository.findById(john.getId())) + .thenReturn(emp); + Mockito.when(employeeRepository.findAll()) + .thenReturn(allEmployees); + Mockito.when(employeeRepository.findById(-99L)) + .thenReturn(Optional.empty()); + } + + @Test + public void whenValidName_thenEmployeeShouldBeFound() { + Optional fromDb = employeeService.getEmployeeByName("john"); + assertThat(fromDb.get() + .getName()).isEqualTo("john"); + + verifyFindByNameIsCalledOnce("john"); + } + + @Test(expected = NoSuchElementException.class) + public void whenInValidName_thenEmployeeShouldNotBeFound() { + Optional fromDb = employeeService.getEmployeeByName("wrong_name"); + fromDb.get(); + + verifyFindByNameIsCalledOnce("wrong_name"); + } + + @Test + public void whenValidName_thenEmployeeShouldExist() { + boolean doesEmployeeExist = employeeService.exists("john"); + assertThat(doesEmployeeExist).isEqualTo(true); + + verifyFindByNameIsCalledOnce("john"); + } + + @Test + public void whenNonExistingName_thenEmployeeShouldNotExist() { + boolean doesEmployeeExist = employeeService.exists("some_name"); + assertThat(doesEmployeeExist).isEqualTo(false); + + verifyFindByNameIsCalledOnce("some_name"); + } + + @Test + public void whenValidI_thendEmployeeShouldBeFound() { + Optional fromDb = employeeService.getEmployeeById(11L); + assertThat(fromDb.get() + .getName()).isEqualTo("john"); + + verifyFindByIdIsCalledOnce(); + } + + @Test(expected = NoSuchElementException.class) + public void whenInValidId_thenEmployeeShouldNotBeFound() { + Optional fromDb = employeeService.getEmployeeById(-99L); + verifyFindByIdIsCalledOnce(); + fromDb.get(); + } + + @Test + public void given3Employees_whengetAll_thenReturn3Records() { + Employee alex = new Employee("alex"); + Employee john = new Employee("john"); + Employee bob = new Employee("bob"); + + List allEmployees = employeeService.getAllEmployees(); + verifyFindAllEmployeesIsCalledOnce(); + assertThat(allEmployees).hasSize(3) + .extracting(Employee::getName) + .contains(alex.getName(), john.getName(), bob.getName()); + } + + private void verifyFindByNameIsCalledOnce(String name) { + Mockito.verify(employeeRepository, VerificationModeFactory.times(1)) + .findByName(name); + Mockito.reset(employeeRepository); + } + + private void verifyFindByIdIsCalledOnce() { + Mockito.verify(employeeRepository, VerificationModeFactory.times(1)) + .findById(Mockito.anyLong()); + Mockito.reset(employeeRepository); + } + + private void verifyFindAllEmployeesIsCalledOnce() { + Mockito.verify(employeeRepository, VerificationModeFactory.times(1)) + .findAll(); + Mockito.reset(employeeRepository); + } +} diff --git a/spring-boot/src/test/java/org/baeldung/boot/boottest/JsonUtil.java b/spring-boot/src/test/java/org/baeldung/boot/boottest/JsonUtil.java new file mode 100644 index 0000000000..3d532ce54a --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/boot/boottest/JsonUtil.java @@ -0,0 +1,14 @@ +package org.baeldung.boot.boottest; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonUtil { + public static byte[] toJson(Object object) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsBytes(object); + } +} diff --git a/spring-boot/src/test/resources/application-integrationtest.properties b/spring-boot/src/test/resources/application-integrationtest.properties new file mode 100644 index 0000000000..508acf45e0 --- /dev/null +++ b/spring-boot/src/test/resources/application-integrationtest.properties @@ -0,0 +1,2 @@ +spring.datasource.url = jdbc:h2:mem:test +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect \ No newline at end of file