Spring Data Rest testing - spring

I'm developing the application by using Spring Data Rest. As you know, after creating simple repository interfaces, rest-endpoints are created by library.
Do I need to test these endpoints by integration tests? If yes, please provide any examples

here is the code snippet. read the full tutorial here
#Entity
#Table(name = "person")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min = 3, max = 20)
private String name;
// standard getters and setters, constructors
}
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
public Employee findByName(String name);
}
#RunWith(SpringRunner.class)
#SpringBootTest(
SpringBootTest.WebEnvironment.MOCK,
classes = Application.class)
#AutoConfigureMockMvc
#TestPropertySource(
locations = "classpath:application-integrationtest.properties")
public class EmployeeRestControllerIntegrationTest {
#Autowired
private MockMvc mvc;
#Autowired
private EmployeeRepository repository;
#Test
public void givenEmployees_whenGetEmployees_thenStatus200()
throws Exception {
createTestEmployee("bob");
mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content()
.contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$[0].name", is("bob")));
}
}

Aside from regular testing you can do with Spring (using MockMvc, RestAssured, RestTemplate etc), Traverson is a great API for specifically testing Spring HATEOAS. You can use it to test the validity of the links you are returning to your clients, see this example

Related

Mockito injecting mocks Spring Boot test

Hi I have a service class that contains mapper and repository:
#Service
public class ProductServiceImp implements ProductService {
#Autowired
private ProductRepository repository;
#Autowired
private WarehouseApiMapper mapper;
public ProductServiceImp(ProductRepository repository) {
this.repository = repository;
}
}
Repository:
#Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
}
Mapper:
#Mapper(componentModel = "spring")
public interface WarehouseApiMapper {
WarehouseApiMapper mapper = Mappers.getMapper(WarehouseApiMapper.class);
Product ProductDtoToProduct(ProductDto productDto);
ProductDto ProductToProductDto(Product product);
}
In test class I would like to inject mock repository and autowired mapper
Here is my test class:
#SpringBootTest
public class ProductServiceTest {
#Mock
ProductRepository repository;
#InjectMocks
ProductServiceImp service;
#ParameterizedTest
#MethodSource("provideParametersProductUpdate")
void assert_that_product_is_updated_correctly(String productName, BigDecimal productPrice) {
Product oldProduct = new Product("Product that does not exist", BigDecimal.valueOf(1000000), null);
oldProduct.setId(1);
Mockito.when(repository.findById(1)).thenReturn(Optional.of(oldProduct));
Product newProduct = new Product(productName, productPrice, null);
newProduct.setId(1);
ProductDto updatedProduct = service.updateProduct(newProduct);
Assertions.assertEquals(productPrice, updatedProduct.getPrice());
Assertions.assertEquals(productName, updatedProduct.getName());
}
private static Stream<Arguments> provideParametersProductUpdate() {
return Stream.of(
Arguments.of("dark chocolate", BigDecimal.valueOf(3.2)),
Arguments.of("chewing gum", BigDecimal.valueOf(1.2)),
Arguments.of("lollipop", BigDecimal.valueOf(4.0))
);
}
}
Code throws NullPointerException when is trying to map object in service method.
Somebody knows how can I inject this? Thanks for ur answers
If you want to create just a Mockito test you could use the annotation #RunWith(MockitoJUnitRunner.class) instead of #SpringBootTest.
But if you want to create a Spring Boot integration test then you should use #MockBean instead of #Mock and #Autowired instead of #InjectMocks.

Injecting one MockBean into another

I have a typical SpringApplication which I am trying to test via MockMvc. The application contains some database calls and some thridparty api calls, and I want to mock all of them, while testing end to end flow, except thirdparty
This is what I have created -
Controller class
public class PortfolioController {
private final PortfolioService portfolioService;
}
Service Class
public class PortfolioService {
private final PortfolioTransactionRepository portfolioTransactionRepository;
private final AlphavantageService alphavantageService;
}
AlphaVantageService
public class AlphavantageService {
private ApiConfig apiConfig;
private final RestTemplate restTemplate;
public Map<String, List<Candle>> getStockQuotes(List<String> symbols) {
return symbols.stream().collect(Collectors.toMap(symbol -> symbol, symbol -> getQuotes(symbol)));
}
}
Now comes the test -
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = PortfolioController.class)
class PortfolioControllerTest {
private List<PortfolioTransaction> transactions;
#MockBean
private AlphavantageService alphavantageService;
#MockBean
private PortfolioService portfolioService;
#Autowired
private PortfolioController portfolioController;
#Autowired
private MockMvc mockMvc;
}
The problem is, when I try to execute any mvc call on server, AlphaVantageService is not injected inside PortfolioService, so till level1, I get the beans injected, but on further levels, I dont get the same.
Is it by design or I am missing something? How should we test such test-cases?
Actually After trying some options here and there, I found a solution.
Just like #MockBean, spring also have a notion called #SpyBean. That solved my problem. So now my test looks like below
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = PortfolioController.class)
#MockBeans(value = {#MockBean(AlphavantageService.class),
#MockBean(PortfolioTransactionRepository.class)})
#SpyBeans(value = {#SpyBean(PortfolioService.class)})
class PortfolioControllerTest {
private List<PortfolioTransaction> transactions;
#Autowired
private AlphavantageService alphavantageService;
#Autowired
#SpyBean
private PortfolioService portfolioService;
#Autowired
private PortfolioController portfolioController;
#Autowired
private MockMvc mockMvc;
}
This works like a charm and I can use full fledged dependency Injection in the tests.

RestFulClient CRUDRepository Implementation

I am using SpringBoot to write web service client. How to use CRUDRepository inside the client? I have to save the data from the webservice to a database table? How do I get the handle to BookRepository inside my client?
public interface BookRepository extends CrudRepository<BooksStage, Long> {}
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Books[]> responseEntity = restTemplate.getForEntity("http://localhost:8080/booksList", Books[].class);
Books[] books=responseEntity.getBody();
if(books!=null){
log.info("booksreceived -"+books.length);
for (Books c : books){
//Insert into database
}
}
1) Interface BookRepository shall be annotated with #Repository. You should autowire interface BookRepository in your Web Service Client class.
2) The RestTemplate shall be inside a Web Service Client class annotated with #Service
3) Since your entity name is BooksStage, you should only save entity of type BooksStage (and not Books).
#Repository
public interface BookRepository extends CrudRepository<BooksStage, Long> {
}
#Service
public class MyWebServiceClient{
#Autowired
private BookRepository bookRepo;
public boolean updateSomeBook() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<BooksStage[]> responseEntity =
restTemplate.getForEntity ("http://localhost:8080/booksList", Books[].class);
BooksStage[] books=responseEntity.getBody();
if(books!=null){
log.info("booksreceived -"+books.length);
for (BooksStage c : books){
bookRepo.save(c);
}
return true;
}
}
#Entity
#Table(name = "books_stage")
public class BooksStage {
#Id
#Column(name = "ID")
private long id;
//other related fields
}

How to mock Spring Data and unit test service

I'm trying to unit test a service method. The service methods calls a spring data repository method to fetch some data. I want to mock that repository call, and supply the data myself. How to do that? Following Spring Boot documentation, when I mock the repository and call the repository method directly in my test code, the mock is working. But when I call the service method, which in turn would call the repository method, mocking isn't working. Below is the sample code:
Service class:
#Service
public class PersonService {
private final PersonRepository personRepository;
#Autowired
public PersonService(personRepository personRepository) {
this.personRepository = personRepository;
}
public List<Person> findByName(String name) {
return personRepository.findByName(name); // I'd like to mock this call
}
}
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
// http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans
#MockBean
private PersonRepository personRepository;
#Autowired
private PersonService personService;
private List<Person> people = new ArrayList<>();
#Test
public void contextLoads() throws Exception {
people.add(new Person());
people.add(new Person());
given(this.personRepository.findByName("Sanjay Patel")).willReturn(people);
assertTrue(personService.findByName("Sanjay Patel") == 2); // fails
}
}
For Spring Data repositories you need to specifiy the bean name. Mocking via type doesn't seem to work because the repository is a dynamic proxy at runtime.
The default bean name for PersonRepository is "personRepository", so this should work:
#MockBean("personRepository")
private PersonRepository personRepository;
Here's the complete test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
// http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans
#MockBean("personRepository")
private PersonRepository personRepository;
#Autowired
private PersonService personService;
private List<Person> people = new ArrayList<>();
#Test
public void contextLoads() throws Exception {
people.add(new Person());
people.add(new Person());
given(this.personRepository.findByName("Sanjay Patel")).willReturn(people);
assertTrue(personService.findByName("Sanjay Patel") == 2); // fails
}
}
Probably the repository is marked with #MockedBean annotation. I do not know if Spring can auto wire by type if the repository is a mock.
You can define the #Bean method and return Mockito.mock(X.class), this should work.
Not sure you need spring for unit testing a service method though. A lighter approach would be to use solely Mockito with its #InjectMocks annotation.

Unit testing field validation with MockMvc but without Spring context?

Is it possible to test #Valid annotations using MockMvc and MockitoJUnitRunner? I can test most of the behavior of my CRUD controller but validation seems to require the use of Spring's JUnit runner, building the entire context and creating the JPA repo implementation which requires a lot of stuff.
The test below tries to test a POST method receiving a Customer entity where the firstName field is annotated with #Size(min=2, max=20). The result is
java.lang.AssertionError: View name expected:<edit> but was:<redirect:/info>
So the validation did not run.
#RunWith(MockitoJUnitRunner.class)
public class DataControllerTest {
#Mock
CustomerRepository mockRepo;
#InjectMocks
private DataController controller;
MockMvc mockmvc;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
mockmvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testBadSubmit() throws Exception {
mockmvc.perform(MockMvcRequestBuilders.post("/edit/1")
.param("firstName", "a"))
.andExpect(MockMvcResultMatchers.view().name("edit"));
Mockito.verifyZeroInteractions(mockRepo);
}
}
Controller class:
#Controller
public class DataController {
#Autowired
public CustomerRepository crep;
...
#RequestMapping(value = {"/edit/{id}"}, method = RequestMethod.POST)
public String add(Model model, #Valid Customer customer, Errors result) {
if (result.hasErrors()) {
return "edit";
}
crep.save(customer);
return "redirect:/info";
}
Entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id = null;
#Column(length=20)
#Size(min=2, max=20)
private String firstName;
...
}
JPA Repository interface:
#Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
The purpose of the SpringJUnit4ClassRunner is to automatically load the application context and wire up everything automatically. You should be able to use MockitoJUnitRunner but you'll have to load the application context you want to use manually in your test. Regardless, however, you do need to load an application context, because calling DataController#add() through Spring is the only way that the #Valid annotation will be processed.
EDIT: If the real problem here is loading the JPA repository, you can use MockitoJUnitRunner and just load a test application context where a mock JPA repository is manually wired in, at little to no expense.

Resources