# Spring Boot code ###### tags: `CTBC-Spring Boot` `CTBC` :::spoiler ```java ``` ::: ## src/main/java ### com.ctbc.boot.lab1 #### Lab1Application :::spoiler ```java package com.ctbc.boot.lab1; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication // @ -> interface // P.6 public class Lab1Application { public static void main(String[] args) { SpringApplication.run(Lab1Application.class, args); } } ``` ::: ### com.ctbc.boot.lab1.bean :::spoiler CourseForm ```java package com.ctbc.boot.lab1.bean; import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import lombok.Data; @Data public class CourseForm { // 加上限制 @NotEmpty // @ 含有interface的概s念 @Size(min=5,max=30) private String courseId; @NotEmpty private String courseName; @NotNull @Min(7) private Integer duration; } ``` ::: :::spoiler Customer ```java package com.ctbc.boot.lab1.bean; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; //1 @NoArgsConstructor @AllArgsConstructor @Data //2 @Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String mobliePhone; private String email; } ``` ::: :::spoiler Employee ```java package com.ctbc.boot.lab1.bean; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @AllArgsConstructor // 所有參數的建構子 @NoArgsConstructor // 對Spring Boot很重要 @Data public class Employee { private String serial; private String name; private int grade; } ``` ::: :::spoiler Message ```java package com.ctbc.boot.lab1.bean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Component // 加上後,就會變成Spring元件 // 1 @Data @AllArgsConstructor @NoArgsConstructor public class Message { // 2 private int id; private String name; } ``` ::: :::spoiler NormalUser1 ```java package com.ctbc.boot.lab1.bean; import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class NormalUser1 { @NotNull(message="Name should not be null") private String name; } ``` ::: :::spoiler NormalUser2 ```java package com.ctbc.boot.lab1.bean; import javax.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class NormalUser2 { @NotEmpty(message = "Name should not be empty") private String name; } ``` ::: :::spoiler NormalUser3 ```java package com.ctbc.boot.lab1.bean; import javax.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class NormalUser3 { @NotBlank(message = "Name may not be blank") private String name; } ``` ::: ### com.ctbc.boot.lab1.calculator :::spoiler AddCalculator ```java package com.ctbc.boot.lab1.calculator; import org.springframework.stereotype.Component; @Component("addBean") public class AddCalculator implements Calculator { @Override public int calc(int a, int b) { return a + b; } } ``` ::: :::spoiler Calculator ```java package com.ctbc.boot.lab1.calculator; import org.springframework.stereotype.Component; @Component public interface Calculator { int calc(int a, int b); } ``` ::: :::spoiler SubCalculator ```java package com.ctbc.boot.lab1.calculator; import org.springframework.stereotype.Component; @Component("subtractBean") public class SubCalculator implements Calculator{ @Override public int calc(int a, int b) { return a - b; } } ``` ::: ### com.ctbc.boot.lab1.config :::spoiler AppConfig (V) ```java package com.ctbc.boot.lab1.config; // 設定檔 import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.ctbc.boot.lab1.calculator.AddCalculator; import com.ctbc.boot.lab1.calculator.Calculator; import com.ctbc.boot.lab1.calculator.SubCalculator; @Configuration public class AppConfig { // 直接使用interface // 一個介面只能有1個Bean @Bean("addBean") public Calculator calculator1 () { return new AddCalculator(); } @Bean("subtractBean") public Calculator calculator2() { return new SubCalculator(); } } ``` ::: ### com.ctbc.boot.lab1.controllers :::spoiler CourseController ```java package com.ctbc.boot.lab1.controllers; import javax.validation.Valid; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.ctbc.boot.lab1.bean.CourseForm; import lombok.extern.slf4j.Slf4j; @Controller // 1 @Slf4j public class CourseController implements WebMvcConfigurer{ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/results").setViewName("results"); // url,name } @GetMapping("/course") // 2 public String showCourse(CourseForm courseForm) { // CourseForm courseform // courseForm.setCourseId("POOP"); // courseForm.setCourseName("Python物件導向實務"); // courseForm.setDuration(35); return "course"; } @PostMapping("/course") public String checkCourse(@Valid CourseForm y, BindingResult bindingResult) { log.info("get course from HTML form : {}",y); if (bindingResult.hasErrors()) { log.info("validation got errors, need to fix"); return "course"; } return "redirect:/results"; // 這邊應該是個成功網頁的URL } } ``` ::: :::spoiler CustomerRestController ```java package com.ctbc.boot.lab1.controllers; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.ctbc.boot.lab1.bean.Customer; import com.ctbc.boot.lab1.services.CustomerService; import lombok.extern.slf4j.Slf4j; @Slf4j @RestController @RequestMapping("/api/customers") public class CustomerRestController { @Autowired public CustomerService service1; // 讀出 @GetMapping("") public List<Customer> getAllCustomers(){ return service1.findAll(); } // 查詢一筆 @GetMapping(value = "{id}") public Customer getACustomer(@PathVariable Long id) { return service1.findOne(id); } // 寫入 @PostMapping("") @ResponseStatus(HttpStatus.CREATED) public Customer createCustomer(@RequestBody Customer customer) { log.info("create from postman : " + customer.toString()); return service1.create(customer); } // 修改一筆 @PutMapping(value = "{id}") public Customer putCustomer(@PathVariable Long id ,@RequestBody Customer customer) { customer.setId(id); // 確保資料會更新在這筆id上 return service1.update(customer); } // 刪除 @DeleteMapping(value = "{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void delCustomer(@PathVariable Long id) { service1.delete(id); } } ``` ::: :::spoiler GreetingController ```java package com.ctbc.boot.lab1.controllers; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller public class GreetingController { private static final String NAME = "nameHTML"; private static final String PROMPT = "promptHTML"; @RequestMapping("/hello") public String doGreet( @RequestParam(value = "name",required = false,defaultValue = "Brain") String name, @RequestParam(value = "prompt",required = false,defaultValue = "hello") String prompt, Model model) { //name = "Mark"; model.addAttribute(NAME,name); model.addAttribute(PROMPT,prompt); return "greeting"; } } ``` ::: :::spoiler HelloController ```java package com.ctbc.boot.lab1.controllers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; // P.69 import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.ctbc.boot.lab1.bean.Message; @RestController @RequestMapping("/api/hello") public class HelloController { @Autowired private Message m1; @GetMapping("") public Message getHello() { m1.setId(555); m1.setName("normal"); return m1; } // @RequestMapping("/api/hello") // @RequestMapping(path = "/api/hello") // @RequestMapping(path = "/api/hello",method = RequestMethod.GET) // @GetMapping("") // "error": "Method Not Allowed", // public String getHello() { // return "hello_get"; // } @PostMapping("/{id}") // 接參數進來 public String postHello(@PathVariable String id) { return "should_get_a_hello, id = " +id; } @PutMapping("") public String putHello() { return "hello_put"; } @PatchMapping("") public String patchHello() { return "hello_patch"; } @DeleteMapping("") public String deleteHello() { return "hello_delete"; } } ``` ::: :::spoiler InfoController ```java package com.ctbc.boot.lab1.controllers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller // 1 public class InfoController { @GetMapping("/info") // 3 // 2 public String getInfo() { return "info"; } } ``` ::: :::spoiler MainController ```java package com.ctbc.boot.lab1.controllers; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; // spring import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; // spring import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.ctbc.boot.lab1.services.GreetingServices; @Controller public class MainController { @GetMapping("/") public String home1(Model model) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd --> HH:mm:ss"); LocalDateTime now = LocalDateTime.now(); // 拿到server的時間 String message = String.format("現在時刻 : %s", formatter.format(now)); model.addAttribute("messageHTML",message); return "home"; // 此處需return html的檔名 } @Autowired // 別人已經初始化了 // 1 private GreetingServices service; @RequestMapping("/greeting") public @ResponseBody String greeting() { // 2 return service.greet(); } } ``` ::: :::spoiler RootRestController ```java package com.ctbc.boot.lab1.controllers; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ctbc.boot.lab1.bean.CourseForm; @RestController // 1 public class RootRestController { @RequestMapping("/api") String apiHome() { return "Hello api home"; } @RequestMapping("/api/course/0") CourseForm getCourse() { CourseForm courseForm = new CourseForm(); courseForm.setCourseId("POOP"); courseForm.setCourseName("Python"); courseForm.setDuration(35); return courseForm; } } ``` ::: :::spoiler WelcomeController ```java package com.ctbc.boot.lab1.controllers; import org.springframework.beans.factory.annotation.Autowired; // P.70 import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ctbc.boot.lab1.bean.Message; @RestController @RequestMapping("/api/welcome") public class WelcomeController { // @GetMapping("") // public String getWelcome() { // return "welcome_in_get"; // } @Autowired private Message m2; @GetMapping("") public Message getWelcome() { return m2; } } ``` ::: ### com.ctbc.boot.lab1.repository :::spoiler CustomerRepository ```java package com.ctbc.boot.lab1.repository; import org.springframework.data.jpa.repository.JpaRepository; //import org.springframework.data.repository.CrudRepository; import com.ctbc.boot.lab1.bean.Customer; public interface CustomerRepository extends JpaRepository<Customer, Long>{ /* * Finished Spring Data repository scanning in 8 ms. Found 1 JPA repository interfaces * HikariPool-71 - Starting... (CP連線池) * H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:d9d44943-7084-4a1a-960e-4945bbf6b127' */ // // } ``` ::: ### com.ctbc.boot.lab1.runner :::spoiler BeanRunner ```java package com.ctbc.boot.lab1.runner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import com.ctbc.boot.lab1.calculator.Calculator; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class BeanRunner implements CommandLineRunner{ // @Component 對應 @Autowired // com.ctbc.boot.lab1.calculator.Calculator.java 對應 com.ctbc.boot.lab1.runner.BeanRunner.java @Autowired // 不使用Context @Qualifier("addBean") private Calculator calc1; // 要個別@Autowired @Autowired @Qualifier("subtractBean") private Calculator calc2; @Override public void run(String... args) throws Exception { int x = 5; int y = 6; log.info("using @Autowired (instead of @ApplicationContext)"); log.info("{} + {} = {}",x,y,calc1.calc(x, y)); log.info("{} - {} = {}",x,y,calc2.calc(x, y)); } } ``` ::: :::spoiler CusomterRunner ```java package com.ctbc.boot.lab1.runner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import com.ctbc.boot.lab1.bean.Customer; import com.ctbc.boot.lab1.repository.CustomerRepository; import lombok.extern.java.Log; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class CusomterRunner implements CommandLineRunner{ @Autowired private CustomerRepository repository; @Override public void run(String... args) throws Exception { // 放入幾筆資料 initData(); // 取得資料 // SELECT * FROM CUSTOMER // 兩種取得資料的方式 for (Customer customer:repository.findAll()) { log.info("get a customer:{}",customer); } repository.findAll().forEach(customer ->log.info("**{}",customer)); } private void initData() { Customer customer1 = new Customer(); customer1.setLastName("Ho"); customer1.setFirstName("Mark"); Customer customer2 = new Customer(); customer2.setLastName("Chiang"); customer2.setFirstName("Brian"); repository.save(customer1); repository.save(customer2); ; } } ``` ::: :::spoiler InitRunner ```java package com.ctbc.boot.lab1.runner; import org.slf4j.Marker; import org.springframework.boot.CommandLineRunner; // spring boot import org.springframework.stereotype.Component; import com.ctbc.boot.lab1.bean.Employee; import lombok.extern.slf4j.Slf4j; @Slf4j // Simple Logging Facade(門面) for Java @Component public class InitRunner implements CommandLineRunner{ @Override public void run(String... args) throws Exception { //System.out.println("one term startup script will put here"); // 一些一次性初始化的工作寫在這裡 /* log.info("one term startup script will put here"); log.debug("debug"); log.warn("warn"); log.error("error"); */ int x = 5; float y = 3.14f; String z = "hi"; //log.info("x = {}, y = {}, z = {}",x,y,z); // Employee mark = new Employee(); Employee brian = new Employee("Y000456","Brain Chiang",75); //log.info("mark = {}, brain = {}",mark,brian); } } ``` ::: ### com.ctbc.boot.lab1.servieces :::spoiler CustomerService ```java package com.ctbc.boot.lab1.services; import java.util.List; import com.ctbc.boot.lab1.bean.Customer; public interface CustomerService { public List<Customer> findAll(); public Customer findOne(Long id); public Customer create(Customer customer); public Customer update(Customer customer); public void delete(Long id); } ``` ::: :::spoiler GreetingServices ```java package com.ctbc.boot.lab1.servieces; import org.springframework.stereotype.Service; @Service public class GreetingServices { public String greet() { // Spring 最小單位為物件, return "[waiting...] 這是在service中的值,是abcde12345"; } } ``` ::: :::spoiler CustomerServiceJDAImplement ```java package com.ctbc.boot.lab1.services; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ctbc.boot.lab1.bean.Customer; import com.ctbc.boot.lab1.repository.CustomerRepository; @Service public class CustomerServiceJDAImplement implements CustomerService{ @Autowired private CustomerRepository repository; @Override public List<Customer> findAll() { return repository.findAll(); } @Override public Customer findOne(Long id) { // 可能會傳回Customer,可能會傳回null Optional<Customer> optionalCustomer = repository.findById(id); return optionalCustomer.get(); } @Override public Customer create(Customer customer) { Customer savedCustomer = repository.save(customer); return savedCustomer; } // 為什麼不用指定id @Override public Customer update(Customer customer) { Customer savedCustomer = repository.save(customer); return savedCustomer; } @Override public void delete(Long id) { repository.deleteById(id); } } ``` ::: ## src/main/resources :::spoiler application.properties ``` # this is a comment # set spring boot behavior #server.servlet.context-path = /app1 ## change server port #server.port = 8880 spring.jpa.show-sql = true spring.jpa.properties.hibernate.format_sql = true ``` ::: ### templates :::spoiler course.html ```html <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF8"> <title>課程管理介面</title> </head> <body> <h1>課程表單</h1> <form th:object="${courseForm}" method="post" th:action="@{/course}"> <!-- 此處的c需為小寫 --> <table> <tr> <td>課程ID :</td> <td><input type="text" th:field="*{courseId}" /></td> <td th:errors="*{courseId}">如果ID錯誤,在此顯示</td> </tr> <tr> <td>課程名稱 :</td> <td><input type="text" th:field="*{courseName}" /></td> <td th:errors="*{courseName}">如果Name錯誤,在此顯示</td> </tr> <tr> <td>課程時數 : </td> <td><input type="text" th:field="*{duration}" /></td> <td th:errors="*{duration}">如果duration錯誤,在此顯示</td> </tr> <tr> <td><button type="submit">submit</button></td> </tr> </table> </form> </body> </html> ``` ::: :::spoiler greeting.html ```html <!DOCTYPE html> <html> <head> <meta charset="BIG5"> <title>Insert title here</title> </head> <body> <p th:text="${promptHTML} + ' : ' + ${nameHTML}"></p> </body> </html> ``` ::: :::spoiler home.html ```java <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmls="http://www.thymeleaf.org"> <body> <h1>純手刻的html</h1> <img src="/images/img2.png" /> <!-- <img src="myapp1/" --> <br /> <h2>使用thymeleaf</h2> <h2 th:text="${messageHTML}"></h2> <!-- 輸出message --> <img th:src="@{/images/img3.png}" /> </body> </html> ``` ::: :::spoiler info.html ```html <!DOCTYPE html> <html> <head> <meta charset="BIG5"> </head> <title>Insert title here</title> <body> <h2>Information</h2> <h3>Spring Boot???{??</h3> </body> </html> ``` ::: :::spoiler results.html ```html <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF8"> </head> <title>新增成功</title> <body> <h2>課程新增成功</h2> </body> </html> ``` ::: ## src/test/java :::spoiler Lab1ApplicationTests ```java package com.ctbc.boot.lab1; import org.assertj.core.api.Assertions; // assertj import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import com.ctbc.boot.lab1.controllers.MainController; @SpringBootTest class Lab1ApplicationTests { @Autowired // 會被Autowired叫進來 private MainController controller; @Test void contextLoads() { Assertions.assertThat(controller).isNotNull(); } } ``` ::: :::spoiler Lab1Test ```java package com.ctbc.boot.lab1; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; // 需使用junit.jupiter import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import com.ctbc.boot.lab1.bean.Employee; public class Lab1Test { // 理論上要全對 // All //-------------------------------------------------- @BeforeAll // org.junit.platform.commons.JUnitException: @BeforeAll method 'public void com.ctbc.boot.lab1.Lab1Test.oneTermSetup()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS). public static void oneTermSetup() { System.out.println("一次性的啟動"); } @AfterAll public static void finalCleanUp() { System.out.println("\n一次性的清除"); } // Each //-------------------------------------------------- @BeforeEach public void prepare() { System.out.println("\n開始準備"); } @AfterEach public void storeAndClean() { System.out.println("清除並重製"); } //-------------------------------------------------- @Test public void compareObjectIdentical() { /* * assertTure * assertFalse * assertEquals * assertArrayEquals // * assertNotEquals * assertSame // * assertTimeOut */ //Assertions.fail("失敗"); System.out.println("compare object Identical"); Employee e1 = new Employee(); Employee e2 = new Employee(); Employee e3 = e1; // e1 <--> e3, e2 <--> e3 Assertions.assertSame(e1,e3); } @Test public void compareObjectEquality() { System.out.println("compare object equality"); Employee e1 = new Employee("AB1234","Brain",75); Employee e2 = new Employee("AB1234","Brain",75); Assertions.assertEquals(e1, e2); // 因為加了@Data,@Data已實作EqualsAndHashCode,所以會相等 } @Test @Disabled("will be available in next release") public void compareObjectSomething() { System.out.println("compare object certain attribute"); } } ``` ::: :::spoiler MainControllerTest ```java package com.ctbc.boot.lab1; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; //---------------------------------------------------------------------------------------- import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import com.ctbc.boot.lab1.controllers.InfoController; import com.ctbc.boot.lab1.controllers.MainController; import com.ctbc.boot.lab1.services.GreetingServices; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; // import static // ---------------------------------------------------------------------------------------- import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; // 會自動import static,因為Editor -> Content Assist -> Favorites 已建立好 import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.mockito.ArgumentMatchers.contains; // import static org.mockito.Mockito.when; //---------------------------------------------------------------------------------------- //@SpringBootTest //@AutoConfigureMockMvc @WebMvcTest({MainController.class,InfoController.class}) // 只會啟動Web的tier // MainControllerTest.testNotFoundPage:54 Status expected:<200> but was:<404> public class MainControllerTest { @Autowired private MockMvc mockMvc; @MockBean private GreetingServices service; @Test public void testMockMVCanotNull() { assertThat(mockMvc).isNotNull(); // 檢查mockMvc是否為空 } @Test // 測試網頁是否可以執行 public void testHomePage() throws Exception { mockMvc.perform(get("/")) // MainControllerTest.testHomePage:28 Status expected:<200> but was:<404> .andExpect(status().isOk()) .andExpect(view().name("home")) .andExpect(content() .string(Matchers.containsString("手刻"))); // not containing "程式生成" } @Test public void testNotFoundPage() throws Exception { mockMvc.perform(get("/info")) .andExpect(MockMvcResultMatchers.status().isOk()); } @Test public void testResponseBody() throws Exception { when(service.greet()).thenReturn("sample greeting message"); mockMvc.perform(get("/greeting")) // mock bean .andDo(MockMvcResultHandlers.print()) .andExpect(status().isOk()) .andExpect(content().string(containsString("sample"))); } } ``` ::: :::spoiler Validation2Test ```java package com.ctbc.boot.lab1; import static org.assertj.core.api.Assertions.assertThat; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import com.ctbc.boot.lab1.bean.NormalUser1; import com.ctbc.boot.lab1.bean.NormalUser2; public class Validation2Test { // validator private static Validator validator; @BeforeAll public static void setupValidatorInstance() { validator = Validation.buildDefaultValidatorFactory().getValidator(); } @Test public void doSomeTestFirst() { //Assertions.fail("確定會在這exception"); NormalUser2 user = new NormalUser2(null); // 錯誤會記在裡面 Set<ConstraintViolation<NormalUser2>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(1); // null,所以有一個錯 } @Test public void doAnotherTest() { NormalUser2 user = new NormalUser2(""); Set<ConstraintViolation<NormalUser2>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(1);// 空字串 -> 1(有錯誤) } @Test public void doElseTest() { NormalUser2 user = new NormalUser2(" "); Set<ConstraintViolation<NormalUser2>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(0); // 多個空白 -> 視為有字串 -> 0(沒有錯誤) } } ``` ::: :::spoiler Validation3Test ```java package com.ctbc.boot.lab1; import static org.assertj.core.api.Assertions.assertThat; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import com.ctbc.boot.lab1.bean.NormalUser1; import com.ctbc.boot.lab1.bean.NormalUser2; import com.ctbc.boot.lab1.bean.NormalUser3; public class Validation3Test { // validator private static Validator validator; @BeforeAll public static void setupValidatorInstance() { validator = Validation.buildDefaultValidatorFactory().getValidator(); } @Test public void doSomeTestFirst() { //Assertions.fail("確定會在這exception"); NormalUser3 user = new NormalUser3(null); // 錯誤會記在裡面 Set<ConstraintViolation<NormalUser3>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(1); // null,所以有一個錯 } @Test public void doAnotherTest() { NormalUser3 user = new NormalUser3(""); Set<ConstraintViolation<NormalUser3>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(1);// 空字串 -> 1(有錯誤) } @Test public void doElseTest() { NormalUser3 user = new NormalUser3(" "); Set<ConstraintViolation<NormalUser3>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(1); // 多個空白 -> 視為有字串 -> 0(沒有錯誤) } } ``` ::: :::spoiler ValidationTest ```java package com.ctbc.boot.lab1; import static org.assertj.core.api.Assertions.assertThat; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import com.ctbc.boot.lab1.bean.NormalUser1; public class ValidationTest { // validator private static Validator validator; @BeforeAll public static void setupValidatorInstance() { validator = Validation.buildDefaultValidatorFactory().getValidator(); } @Test public void doSomeTestFirst() { //Assertions.fail("確定會在這exception"); NormalUser1 user = new NormalUser1(null); // 錯誤會記在裡面 Set<ConstraintViolation<NormalUser1>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(1); // null,所以有一個錯 } @Test public void doAnotherTest() { NormalUser1 user = new NormalUser1(""); Set<ConstraintViolation<NormalUser1>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(0); // 沒有錯,所以寫0是正確的 } @Test public void doElseTest() { NormalUser1 user = new NormalUser1(" "); Set<ConstraintViolation<NormalUser1>> violations = validator.validate(user); assertThat(violations.size()).isEqualTo(0); // 沒有錯,所以寫0是正確的 } } ``` :::