Personal accumulation, please do not reproduce privately, please contact before reproducing
Code and article resources https://github.com/jedyang/DayDayUp/tree/master/java/springboot
Reading notes based on Spring Book CookBook focus on personal understanding and practice rather than translation.
test
For a comprehensive analysis of unit testing, see my article, which only talks about spring boot.
unit testing
The different versions of springboot tested vary considerably. Here we use 1.5.*
In the project we created, the required directory structure BookPub Application Tests already exists.
Test rest requests
package org.test.bookpub; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.test.bookpub.entity.Book; import static junit.framework.TestCase.assertNotNull; import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class)// Spring JUnit support, which introduces Spring-Test framework support @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) public class BookPubApplicationTests { @Autowired private TestRestTemplate restTemplate; @Test public void webappBookIsbnApi() { // Book book = restTemplate.getForObject("/books/1001", Book.class); Book book = restTemplate.getForObject("http://localhost:8080/books/1001", Book.class); System.out.println(book); assertNotNull(book); assertEquals("yunsheng", book.getPublisher().getName()); } }
-
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT is a port that uses configuration.
Another is the webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT random port.
Because restTemplate. getForObject ("http://localhost:8080/books/1001"), Book. class) is written to death url, so any one can be set here. - Book book = restTemplate.getForObject("/books/1001", Book.class); this uses the default protocol and domain name of the code. Because our default configuration is https. There will be problems with direct use here.
Test database connection
Add a test method
@Autowired private BookRepository bookRepository; // Test database can be connected normally @Test public void contextLoads() { assertEquals(1, bookRepository.count()); }
Server-side testing
TestRestTemplate is a test that simulates requests from clients. Another approach is to test the controller layer directly on the server side using MockMvc objects. It's like starting the server.
@Autowired private WebApplicationContext context; private MockMvc mockMvc; @Before public void setupMockMvc() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void webappBookApi() throws Exception { //MockHttpServletRequestBuilder.accept method is to set the content type that the client can recognize. //MockHttpServletRequestBuilder.contentType, which sets the Content-Type field in the request header to represent the content type of the request body mockMvc.perform(get("http://localhost:8080/books/1001") .accept(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(content().string(containsString("yunsheng"))) .andExpect(jsonPath("$.isbn").value("1001")); }
Database insertion data
Previously, we put the code to insert a record into the database in Startup Runner, which is not very appropriate. Now replace it with unit testing.
There are several ways to do this.
- Using mapping tools like hibernate, it scans the classes annotated by @Entity and automatically creates the corresponding tables. Then use import.sql to create data.
- Using spring jdbc, table structure is defined by schema.sql file, and data.sql is used to create data.
practice
Using hibernate
-
Comment out the code in Startup Runner before
@Override public void run(String... strings) throws Exception { logger.info("dataSource:" + ds.toString()); // Add a record // Author author = new Author("yunsheng", "yang"); // author = authorRepository.save(author); // Publisher publisher = new Publisher("yunsheng"); // publisher = publisherRepository.save(publisher); // Book book = new Book("1001", "test1", author, publisher); // Book book1 = bookRepository.save(book); logger.info("number of books:" + bookRepository.count()); }
-
Configuration in application.properties
spring.jpa.hibernate.ddl-auto=create-drop
-
Create import.sql file under resources
INSERT INTO author (id, first_name, last_name) VALUES (1, 'yunsheng', 'yang'); INSERT INTO publisher (id, name) VALUES (1, 'yunsheng'); INSERT INTO book (isbn, title, author_id, publisher_id) VALUES ('1001', 'Spring Boot Recipes', 1,1);
Run the test case and pass it.
Using jdbc mode
-
Configuration in application.properties
spring.jpa.hibernate.ddl-auto=none
Don't do anything even if Hibernate relies on being -
Create schema.sql to create tables
-- Create syntax for TABLE 'author' DROP TABLE IF EXISTS `author`; CREATE TABLE `author` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `first_name` VARCHAR(255) DEFAULT NULL, `last_name` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) ); -- CREATE syntax FOR TABLE 'publisher' DROP TABLE IF EXISTS `publisher`; CREATE TABLE `publisher` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) ); -- CREATE syntax FOR TABLE 'reviewer' DROP TABLE IF EXISTS `reviewer`; CREATE TABLE `reviewer` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `first_name` VARCHAR(255) DEFAULT NULL, `last_name` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) ); -- CREATE syntax FOR TABLE 'book' DROP TABLE IF EXISTS `book`; CREATE TABLE `book` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `description` VARCHAR(255) DEFAULT NULL, `isbn` VARCHAR(255) DEFAULT NULL, `title` VARCHAR(255) DEFAULT NULL, `author_id` BIGINT(20) DEFAULT NULL, `publisher_id` BIGINT(20) DEFAULT NULL, PRIMARY KEY (`id`), CONSTRAINT `FK_publisher` FOREIGN KEY (`publisher_id`) REFERENCES `publisher` (`id`), CONSTRAINT `FK_author` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`) ); -- CREATE syntax FOR TABLE 'book_reviewers' DROP TABLE IF EXISTS `book_reviewers`; CREATE TABLE `book_reviewers` ( `book_id` BIGINT(20) NOT NULL, `reviewers_id` BIGINT(20) NOT NULL, CONSTRAINT `FK_book` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`), CONSTRAINT `FK_reviewer` FOREIGN KEY (`reviewers_id`) REFERENCES `reviewer` (`id`) );
Create data.sql to insert data
Just copy it as import.sqlRun test cases, pass
By default, Hibernate uses import.sql. jdbc uses schema.sql and data.sql.
If you want to replace schema.sql or data.sql with another name, Spring Book also provides the corresponding configuration properties, spring.datasource.schema and spring.datasource.data.
tips, I made a mistake here, writing reviewer as an internal class of publisher, where creating tables will fail and have been modified.
mock database operation
In practice, the more common way we write single test is to mock out database operations. Here's a demonstration of using mockito.
package org.test.bookpub; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; import org.test.bookpub.repository.PublisherRepository; import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class) @SpringBootTest public class PublisherRepositoryTests { @MockBean private PublisherRepository repository; @Before public void setupPublisherRepositoryMock() { Mockito.when(repository.count()).thenReturn(100L); } @Test public void publishersExist() { assertEquals(100, repository.count()); } @After public void resetPublisherRepositoryMock() { Mockito.reset(repository); } }
Create a new class demonstration, mockito more usage, can be google.
I don't really understand a comment here. cookbook has a bunch of code.
In fact, I have regretted writing here. cookbook is too old.
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
Look directly at the official documents. You have everything you want.