mirror of
https://github.com/JeffLi1993/springboot-learning-example.git
synced 2026-03-13 13:33:44 +08:00
完善章节3单元测试
This commit is contained in:
@@ -60,4 +60,23 @@ public class Book implements Serializable {
|
||||
public void setIntroduction(String introduction) {
|
||||
this.introduction = introduction;
|
||||
}
|
||||
|
||||
public Book(Long id, String name, String writer, String introduction) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.writer = writer;
|
||||
this.introduction = introduction;
|
||||
}
|
||||
|
||||
public Book(Long id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Book(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Book() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,4 +42,18 @@ public interface BookService {
|
||||
* @param id 编号
|
||||
*/
|
||||
Book findById(Long id);
|
||||
|
||||
/**
|
||||
* 查找书是否存在
|
||||
* @param book
|
||||
* @return
|
||||
*/
|
||||
boolean exists(Book book);
|
||||
|
||||
/**
|
||||
* 根据书名获取书籍
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
Book findByName(String name);
|
||||
}
|
||||
|
||||
@@ -2,24 +2,40 @@ package demo.springboot.service.impl;
|
||||
|
||||
import demo.springboot.domain.Book;
|
||||
import demo.springboot.service.BookService;
|
||||
import demo.springboot.web.BookController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Book 业务层实现
|
||||
*
|
||||
* <p>
|
||||
* Created by bysocket on 27/09/2017.
|
||||
*/
|
||||
@Service
|
||||
public class BookServiceImpl implements BookService {
|
||||
|
||||
private static final AtomicLong counter = new AtomicLong();
|
||||
|
||||
|
||||
/**
|
||||
* 使用集合模拟数据库
|
||||
*/
|
||||
private static List<Book> books = new ArrayList<>(
|
||||
Arrays.asList(
|
||||
new Book(counter.incrementAndGet(), "book")));
|
||||
|
||||
|
||||
// 模拟数据库,存储 Book 信息
|
||||
// 第五章《数据存储》会替换成 MySQL 存储
|
||||
private static Map<Long, Book> BOOK_DB = new HashMap<>();
|
||||
private static Map<String, Book> BOOK_DB = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public List<Book> findAll() {
|
||||
@@ -29,23 +45,40 @@ public class BookServiceImpl implements BookService {
|
||||
@Override
|
||||
public Book insertByBook(Book book) {
|
||||
book.setId(BOOK_DB.size() + 1L);
|
||||
BOOK_DB.put(book.getId(), book);
|
||||
BOOK_DB.put(book.getId().toString(), book);
|
||||
return book;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Book update(Book book) {
|
||||
BOOK_DB.put(book.getId(), book);
|
||||
BOOK_DB.put(book.getId().toString(), book);
|
||||
return book;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Book delete(Long id) {
|
||||
return BOOK_DB.remove(id);
|
||||
return BOOK_DB.remove(id.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Book findById(Long id) {
|
||||
return BOOK_DB.get(id);
|
||||
return BOOK_DB.get(id.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(Book book) {
|
||||
return findByName(book.getName()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Book findByName(String name) {
|
||||
|
||||
for (Book book : books) {
|
||||
if (book.getName().equals(name)) {
|
||||
return book;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,14 @@ package demo.springboot.web;
|
||||
|
||||
import demo.springboot.domain.Book;
|
||||
import demo.springboot.service.BookService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -16,6 +22,10 @@ import java.util.List;
|
||||
@RequestMapping(value = "/book")
|
||||
public class BookController {
|
||||
|
||||
|
||||
private final Logger LOG = LoggerFactory.getLogger(BookController.class);
|
||||
|
||||
|
||||
@Autowired
|
||||
BookService bookService;
|
||||
|
||||
@@ -43,8 +53,20 @@ public class BookController {
|
||||
* 通过 @RequestBody 绑定实体参数,也通过 @RequestParam 传递参数
|
||||
*/
|
||||
@RequestMapping(value = "/create", method = RequestMethod.POST)
|
||||
public Book postBook(@RequestBody Book book) {
|
||||
return bookService.insertByBook(book);
|
||||
public ResponseEntity<Void> postBook(@RequestBody Book book, UriComponentsBuilder ucBuilder) {
|
||||
|
||||
LOG.info("creating new book: {}", book);
|
||||
|
||||
if (book.getName().equals("conflict")){
|
||||
LOG.info("a book with name " + book.getName() + " already exists");
|
||||
return new ResponseEntity<>(HttpStatus.CONFLICT);
|
||||
}
|
||||
|
||||
bookService.insertByBook(book);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setLocation(ucBuilder.path("/book/{id}").buildAndExpand(book.getId()).toUri());
|
||||
return new ResponseEntity<>(headers, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package demo.springboot.web;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import demo.springboot.WebApplication;
|
||||
import demo.springboot.domain.Book;
|
||||
import demo.springboot.service.BookService;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
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.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WebApplication.class)
|
||||
@AutoConfigureMockMvc
|
||||
@TestPropertySource(locations = "classpath:application.properties")
|
||||
public class BookControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Mock
|
||||
private BookService bookService;
|
||||
|
||||
@InjectMocks
|
||||
private BookController bookController;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mockMvc = MockMvcBuilders
|
||||
.standaloneSetup(bookController)
|
||||
//.addFilters(new CORSFilter())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getBookList() throws Exception {
|
||||
mockMvc.perform(get("/book")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_UTF8))
|
||||
.andExpect(jsonPath("$", hasSize(0)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_create_book_success() throws Exception {
|
||||
|
||||
Book book = createOneBook();
|
||||
|
||||
when(bookService.insertByBook(book)).thenReturn(book);
|
||||
|
||||
mockMvc.perform(
|
||||
post("/book/create")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(asJsonString(book)))
|
||||
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(header().string("location", containsString("/book/1")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_create_book_fail_404_not_found() throws Exception {
|
||||
|
||||
Book book = new Book(99L, "conflict");
|
||||
|
||||
when(bookService.exists(book)).thenReturn(true);
|
||||
|
||||
mockMvc.perform(
|
||||
post("/book/create")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(asJsonString(book)))
|
||||
.andExpect(status().isConflict());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_get_book_success() throws Exception {
|
||||
|
||||
Book book = new Book(1L, "测试获取一本书", "strongant作者", "社区 www.spring4all.com 出版社出版");
|
||||
|
||||
when(bookService.findById(1L)).thenReturn(book);
|
||||
|
||||
mockMvc.perform(get("/book/{id}", 1L))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
||||
.andExpect(jsonPath("$.id", is(1)))
|
||||
.andExpect(jsonPath("$.name", is("测试获取一本书")));
|
||||
|
||||
verify(bookService, times(1)).findById(1L);
|
||||
verifyNoMoreInteractions(bookService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_get_by_id_fail_null_not_found() throws Exception {
|
||||
when(bookService.findById(1L)).thenReturn(null);
|
||||
|
||||
//TODO: 查找不到应该抛出 404 状态码, Demo 待优化
|
||||
mockMvc.perform(get("/book/{id}", 1L))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(""));
|
||||
|
||||
verify(bookService, times(1)).findById(1L);
|
||||
verifyNoMoreInteractions(bookService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_update_book_success() throws Exception {
|
||||
|
||||
Book book = createOneBook();
|
||||
|
||||
when(bookService.findById(book.getId())).thenReturn(book);
|
||||
doReturn(book).when(bookService).update(book);
|
||||
|
||||
mockMvc.perform(
|
||||
put("/book/update", book)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(asJsonString(book)))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_update_book_fail_not_found() throws Exception {
|
||||
Book book = new Book(999L, "测试书名1");
|
||||
|
||||
when(bookService.findById(book.getId())).thenReturn(null);
|
||||
|
||||
mockMvc.perform(
|
||||
put("/book/update", book)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(asJsonString(book)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(""));
|
||||
}
|
||||
|
||||
// =========================================== Delete Book ============================================
|
||||
|
||||
@Test
|
||||
public void test_delete_book_success() throws Exception {
|
||||
|
||||
Book book = new Book(1L, "这本书会被删除啦");
|
||||
|
||||
when(bookService.findById(book.getId())).thenReturn(book);
|
||||
doReturn(book).when(bookService).delete(book.getId());
|
||||
|
||||
mockMvc.perform(
|
||||
delete("/book/delete/{id}", book.getId())
|
||||
).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_delete_book_fail_404_not_found() throws Exception {
|
||||
Book book = new Book(1L, "这本书会被删除啦");
|
||||
|
||||
when(bookService.findById(book.getId())).thenReturn(null);
|
||||
|
||||
mockMvc.perform(
|
||||
delete("/book/delete/{id}", book.getId()))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
|
||||
public static String asJsonString(final Object obj) {
|
||||
try {
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.writeValueAsString(obj);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Book createOneBook() {
|
||||
Book book = new Book();
|
||||
book.setId(1L);
|
||||
book.setName("测试书名1");
|
||||
book.setIntroduction("这是一本 www.spring4all.com 社区出版的很不错的一本书籍");
|
||||
book.setWriter("strongant");
|
||||
return book;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
server.port=9090
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:1.5.1.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:1.5.1.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:1.5.1.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:1.5.1.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.1.9" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.1.9" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:jcl-over-slf4j:1.7.22" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.22" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:log4j-over-slf4j:1.7.22" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="Maven: org.yaml:snakeyaml:1.17" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:1.5.1.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:8.5.11" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:8.5.11" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:8.5.11" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.hibernate:hibernate-validator:5.3.4.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: javax.validation:validation-api:1.1.0.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.3.0.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.3.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.8.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.8.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-web:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-aop:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-beans:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-context:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-expression:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:1.5.1.RELEASE" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:1.5.1.RELEASE" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.2.0" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.2.1" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:1.1" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.ow2.asm:asm:5.0.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.22" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:2.6.0" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:1.10.19" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:2.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-library:1.3" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.4.0" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-core:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:4.3.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: junit:junit:4.12" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:1.4.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:1.5.1.RELEASE" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
Reference in New Issue
Block a user