# 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>[m
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是正確的
}
}
```
:::