<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                后臺的功能與其它實體的添加無異,也是先建立實體然后建立對應的倉庫,再建立serivce,最終建立controller。與前面學習的知識點稍有不同的是每門課程中可以有多個班級,它們間的關系是多對多,這個知識點將放到下節中單獨進行講解。本節除使用前面已學習的知識點完成基本的功能開發外,還將使用**實體監聽器**來替換`@PrePersist`及`@PreUpdate`完成對課程名稱長度的校驗。 # 實體開發 在entity包中新建Course課程實體。 ```java package com.mengyunzhi.springbootstudy.entity; import javax.persistence.*; /** * 課程 * @author panjie */ @Entity public class Course { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String name = ""; @ManyToOne private Teacher teacher; // 省略構造函數及setter/getter } ``` 對Course的測試依賴于倉庫層,下面繼續開發倉庫層。 # 倉庫層開發 repository/CourseRepository.java ```java package com.mengyunzhi.springbootstudy.repository; import com.mengyunzhi.springbootstudy.entity.Course; import org.springframework.data.repository.CrudRepository; public interface CourseRepository extends CrudRepository<Course, Long> { } ``` ## 測試 新建Course實體的測試文件CourseTest.java,然后分別就name字段的unique以及nullable進行驗證。初始化如下: ```java package com.mengyunzhi.springbootstudy.entity; import com.mengyunzhi.springbootstudy.repository.CourseRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest @RunWith(SpringRunner.class) public class CourseTest { @Autowired CourseRepository courseRepository; private Course course; @Before public void before() { } @Test public void save() { } @Test public void nameUniqueTest() { } @Test public void nameNullable() { } } ``` 按前面的思路,若想驗證某個校驗是否生效最好的方法是在@Before先創建一個正常的課程。 entity/CourseTest.java ```java @Before public void before() { this.course = new Course(); this.course.setName(RandomString.make(4)); } @Test public void save() { this.courseRepository.save(this.course); } ``` ### Unique驗證 entity/CourseTest.java ```java @Test(expected = DataIntegrityViolationException.class) public void nameUniqueTest() { this.courseRepository.save(this.course); Course course = new Course(); course.setName(this.course.getName()); this.courseRepository.save(course); } ``` ### Nullable驗證 entity/CourseTest.java ```java @Test(expected = DataIntegrityViolationException.class) public void nameNullable() { this.course.setName(null); this.courseRepository.save(course); } ``` # M層開發 接口初始化 service/CourseService.java ```java package com.mengyunzhi.springbootstudy.service; import com.mengyunzhi.springbootstudy.entity.Course; /** * 課程 * @author panjie */ public interface CourseService { /** * 新增課程 * @param course 課程 * @return 課程 */ Course save(Course course); } ``` 實現類 service/CourseServiceImpl.java ```java @Service public class CourseServiceImpl implements CourseService { private CourseRepository courseRepository; @Autowired public CourseServiceImpl(CourseRepository courseRepository) { this.courseRepository = courseRepository; } @Override public Course save(Course course) { return this.courseRepository.save(course); } } ``` ## 單元測試 初始化如下: service/CourseServiceImplTest.java ```java public class CourseServiceImplTest { private CourseRepository courseRepository; private CourseService courseService; public CourseServiceImplTest() { this.courseRepository = Mockito.mock(CourseRepository.class); this.courseService = new CourseServiceImpl(this.courseRepository); } @Test public void save() { } } ``` 補充測試代碼如下: service/CourseServiceImplTest.java ```java @Test public void save() { Course course = new Course(); Course returnCourse = new Course(); Mockito.when(this.courseRepository.save(course)).thenReturn(returnCourse); Course resultCourse = this.courseService.save(course); Assert.assertEquals(returnCourse, resultCourse); } ``` # C層開發 新建CourseController并初始化如下: controller/CourseController.java ```java @RestController @RequestMapping("Course") public class CourseController { @Autowired CourseService courseService; @PostMapping @ResponseStatus(HttpStatus.CREATED) public Course save(@RequestBody Course course) { return this.courseService.save(course); } } ``` ## 單元測試 初始化單元測試文件Course/CourseControllerTest.java ```java @SpringBootTest @RunWith(SpringRunner.class) @AutoConfigureMockMvc public class CourseControllerTest { @MockBean private CourseService courseService; @Autowired MockMvc mockMvc; @Test public void save() { } } ``` 補充測試代碼如下: Course/CourseControllerTest.java ```java @Test public void save() throws Exception { JSONObject jsonObject = new JSONObject(); String name = RandomString.make(4); jsonObject.put("name", name); String url = "/Course"; Course returnCourse = new Course(); returnCourse.setId(new Random().nextLong()); returnCourse.setName(RandomString.make(4)); returnCourse.setTeacher(new Teacher()); returnCourse.getTeacher().setId(new Random().nextLong()); returnCourse.getTeacher().setName(RandomString.make(4)); Mockito.when(this.courseService.save(Mockito.any(Course.class))).thenReturn(returnCourse); this.mockMvc.perform(MockMvcRequestBuilders.post(url) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(jsonObject.toString()) ).andExpect(MockMvcResultMatchers.status().is(201)) .andExpect(MockMvcResultMatchers.jsonPath("id").value(returnCourse.getId())) .andExpect(MockMvcResultMatchers.jsonPath("name").value(returnCourse.getName())) .andExpect(MockMvcResultMatchers.jsonPath("teacher.id").value(returnCourse.getTeacher().getId())) .andExpect(MockMvcResultMatchers.jsonPath("teacher.name").value(returnCourse.getTeacher().getName())) ; ArgumentCaptor<Course> courseArgumentCaptor = ArgumentCaptor.forClass(Course.class); Mockito.verify(this.courseService).save(courseArgumentCaptor.capture()); Assert.assertEquals(courseArgumentCaptor.getValue().getName(), name); } ``` 測試結果: ``` 請求的地址為/Course請求的方法為:POST 當前token未綁定登錄用戶,返回401 java.lang.AssertionError: Response status Expected :201 Actual :401 ``` ## 401 之所以發生401是由于在上一章中啟用了攔截器進行了用戶認證,而當前的單元測試并沒有傳遞認證成功的authToken。解決這個問題的方法很多,我們采用Mock TeacherService中的isLogin方法來通過認證攔截器。 Course/CourseControllerTest.java ```java @MockBean ? private TeacherService teacherService; @Before public void before() { Mockito.when(this.teacherService.isLogin(Mockito.any())).thenReturn(true); ? } ``` * ? 使用MockBean注入TeacherService。該注解使得在整個測試過程中運行中的teacherService全部為此替身 * ? 當認證攔截器調用替身的isLogin方法時返回true,表示teacher已認證 再次運行單元測試通過。 ## 小作業 其它對控制器的單元測試同樣發生了401錯誤,請參考上面的代碼進行修正。 # 實體監聽器 在前面的章節中使用了`@PrePersist`及`@PreUpdate`完成了對字段長度的校驗,本節中展示另外一種對單元測試更友好的方案:實體監聽器。 ## 初始化 在entity包中新建CourseListener entity/CourseListener.java ```java package com.mengyunzhi.springbootstudy.entity; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; /** * 實體監聽器。當課程實體發生新建、更新操作時執行 */ public class CourseListener { @PrePersist ? public void prePersist(Course course?) { System.out.println("prePersist"); } @PreUpdate ? public void perUpdate(Course course?) { System.out.println("perUpdate"); } } ``` * ? 與在實體中直接使用的注解相同 * ? 參數中必須傳入一個參數,該參數對應的類型為被監聽的實體(在只監聽某一個實體的前提下) 在Course實體上使用EntityListeners注解來添加實體監聽器: entity/Course.java ```java @Entity @EntityListeners(CourseListener.class) public class Course { ``` ### 測試 打到CourseTest建立update方法 entity/CourseTest.java ```java @Test void update() { this.courseRepository.save(this.course); this.course.setName(RandomString.make(4)); this.courseRepository.save(this.course); } ``` 運行單元測試控制臺如下: ``` prePersist perUpdate ``` 說明實體監聽器已生效。 ## 校驗課程長度 entity/CourseListener.java ```java public class CourseListener { @PrePersist public void prePersist(Course course) { if (course.getName() == null || course.getName().length() < 2) { throw new DataIntegrityViolationException("課程名稱長度最小為2位"); } } @PreUpdate public void perUpdate(Course course) { if (course.getName() == null || course.getName().length() < 2) { throw new DataIntegrityViolationException("課程名稱長度最小為2位"); } } } ``` prePersist與perUpdate方法中的代碼是相同的,所以合并如下: entity/CourseListener.java ```java public class CourseListener { @PrePersist @PreUpdate public void prePersistAndUpdate(Course course) { if (course.getName() == null || course.getName().length() < 2) { throw new DataIntegrityViolationException("課程名稱長度最小為2位"); } } } ``` ### 單元測試 entity/CourseTest.java ```java @Test public void nameLength() { Boolean catchException = false; this.course.setName(null); try { this.courseRepository.save(this.course); } catch (DataIntegrityViolationException e) { Assert.assertEquals(e.getMessage(), "課程名稱長度最小為2位"); catchException = true; } Assert.assertTrue(catchException); catchException = false; this.course.setName(RandomString.make(1)); try { this.courseRepository.save(this.course); } catch (DataIntegrityViolationException e) { Assert.assertEquals(e.getMessage(), "課程名稱長度最小為2位"); catchException = true; } Assert.assertTrue(catchException); for (int i = 2; i < 4; i++) { this.course.setName(RandomString.make(i)); this.courseRepository.save(this.course); } } ``` # 總結 本節中使用了已經學習的方法完成了課程的基本保存功能。同時學習了如何使用實體監聽器的方法來對課程名稱的長度進行校驗。在生產環境中,可以對某一實體添加多個監聽器,還可以將一個監聽器添加到多個實體上。是一種靈活、可復用性強的校驗方法。 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step6.1.6](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step6.1.6) | - |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看