---
# System prepended metadata

title: Spring Boot code
tags: [CTBC-Spring Boot, CTBC]

---

# 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是正確的
	}
}

```
:::