{"id":21,"date":"2020-07-05T03:30:00","date_gmt":"2020-07-05T02:30:00","guid":{"rendered":"https:\/\/vukieuanh.com\/?p=21"},"modified":"2020-07-05T03:32:07","modified_gmt":"2020-07-05T02:32:07","slug":"viet-unit-test-service-trong-spring-boot","status":"publish","type":"post","link":"https:\/\/vukieuanh.net\/index.php\/2020\/07\/05\/viet-unit-test-service-trong-spring-boot\/","title":{"rendered":"Vi\u1ebft Unit Test Service trong Spring\u00a0Boot"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Trong b\u00e0i vi\u1ebft n\u00e0y m\u00ecnh s\u1ebd h\u01b0\u1edbng d\u1eabn c\u00e1c b\u1ea1n vi\u1ebft Unit Test t\u1ea7ng Service c\u1ee7a Spring Boot.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Trong Spring \u0111\u1ec3 s\u1eed d\u1ee5ng \u0111\u01b0\u1ee3c m\u00f4i tr\u01b0\u1eddng test ta c\u1ea7n cung c\u1ea5p c\u00e1c th\u01b0 vi\u1ec7n<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>testCompile group: 'com.h2database', name: 'h2', version: '1.4.200'\ntestCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.2.0.RELEASE'\ntestImplementation('org.springframework.boot:spring-boot-starter-test') {\n    exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'\n}<\/code><\/pre>\n\n\n\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\"><div class=\"wp-block-group__inner-container\"><\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>H2<\/strong> l\u00e0 m\u1ed9t c\u01a1 s\u1edf d\u1eef li\u1ec7u quan h\u1ec7 (Relational database) \u0111\u01b0\u1ee3c ph\u00e1t tri\u1ec3n b\u1edfi c\u00f4ng ty H2 Software. H2 \u0111\u01b0\u1ee3c vi\u1ebft b\u1eb1ng ng\u00f4n ng\u1eef <strong>Java<\/strong>, m\u00e3 ngu\u1ed3n m\u1edf, g\u1ecdn nh\u1eb9, mi\u1ec5n ph\u00ed. H2 ph\u00e1t h\u00e0nh phi\u00ean b\u1ea3n \u0111\u1ea7u ti\u00ean v\u00e0o n\u0103m 2005. B\u1ed9 c\u00e0i \u0111\u1eb7t <strong>H2<\/strong> c\u00f3 dung l\u01b0\u1ee3ng r\u1ea5t nh\u1ecf, ch\u1ec9 kho\u1ea3ng <strong>8MB<\/strong>. \u0110\u00e2y l\u00e0 m\u1ed9t&nbsp;<em>in memory database<\/em>. \u0110\u1ea1i kh\u00e1i l\u00e0 m\u1ecdi th\u1ee9 b\u1ea1n l\u01b0u tr\u00ean database ch\u1ec9 t\u1ed3n t\u1ea1i trong b\u1ed9 nh\u1edb, khi t\u1eaft \u1ee9ng d\u1ee5ng, database s\u1ebd m\u1ea5t s\u1ea1ch, th\u00edch h\u1ee3p cho m\u00f4i tr\u01b0\u1eddng test. Th\u01b0 vi\u1ec7n com.h2database ch\u1ec9 \u0111\u01b0\u1ee3c include v\u00e0o d\u1ef1 \u00e1n khi Compile d\u1ef1 \u00e1n \u1edf m\u00f4i tr\u01b0\u1eddng test.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Trong Spring Boot, c\u1ea5u tr\u00fac th\u01b0 m\u1ee5c trong <code>test<\/code> n\u00ean gi\u1ed1ng ho\u00e0n to\u00e0n v\u1edbi c\u1ea5u tr\u00fac th\u01b0 m\u1ee5c trong <code>main<\/code> c\u1ee7a ch\u00fang ta.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/vukieuanh.com\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-04-at-9.52.24-AM-1024x771.png\" alt=\"\" class=\"wp-image-24\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">T\u1ea7ng Service l\u00e0 t\u1ea7ng x\u1eed l\u00fd nghi\u1ec7p v\u1ee5 trong Spring. T\u1ea7ng n\u00e0y d\u00f9ng d\u1eef li\u1ec7u l\u1ea5y \u0111\u01b0\u1ee3c t\u1eeb t\u1ea7ng Repository. V\u00ec v\u1eady \u0111\u1ec3 \u0111\u1ea3m b\u1ea3o test ch\u1ea1y \u0111\u1ed9c l\u1eadp v\u1edbi m\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n, khi test, ch\u00fang ta c\u1ea7n l\u00e0m gi\u1ea3 t\u1ea7ng Repository.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ch\u00fang ta b\u1eaft \u0111\u1ea7u v\u00e0o vi\u1ebft test c\u00e1c b\u1ea1n nh\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u0110\u00e2y l\u00e0 CategoryService c\u1ee7a m\u00ecnh<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Service\npublic class CategoryService implements ICategoryService {\n    @Autowired\n    private ICategoryRepository categoryRepository;\n\n    public CategoryService() {\n    }\n\n    public CategoryService(ICategoryRepository categoryRepository) {\n        this.categoryRepository = categoryRepository;\n    }\n\n\n    @Override\n    public Iterable&lt;Categories> findAll() {\n        return categoryRepository.findAll();\n    }\n\n    @Override\n    public Optional&lt;Categories> findById(Long id) {\n        return categoryRepository.findById(id);\n    }\n\n    @Override\n    public Categories save(Categories categories) {\n        return categoryRepository.save(categories);\n    }\n\n    @Override\n    public void remove(Long id) {\n        categoryRepository.deleteById(id);\n    }\n\n    @Override\n    public Optional&lt;Categories> findByName(String name) {\n        return categoryRepository.findByName(name);\n    }\n\n    @Override\n    public Iterable&lt;Categories> findAllByOutComes(Outcomes outcome) {\n        return categoryRepository.findAllByOutComes(outcome);\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ch\u00fang ta s\u1ebd t\u1ea1o ra class <code>CategoryServiceTest<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u00e1c b\u1ea1n th\u1ea5y l\u00e0 m\u00ecnh s\u1eed d\u1ee5ng  <code>ICategoryRepository<\/code> trong service. V\u00ec v\u1eady ch\u00fang ta s\u1ebd ph\u1ea3i l\u00e0m gi\u1ea3 <code>CategoryRepository<\/code> b\u1eb1ng c\u00e1ch s\u1eed d\u1ee5ng <code>org.mockito.Mockito;<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private ICategoryRepository categoryRepository = Mockito.mock(ICategoryRepository.class);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">T\u01b0 t\u01b0\u1edfng c\u1ee7a&nbsp;<code>Mock<\/code>&nbsp;\u0111\u01a1n gi\u1ea3n l\u00e0 khi mu\u1ed1n test (A g\u1ecdi B) th\u00ec thay v\u00ec t\u1ea1o ra m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng B th\u1eadt s\u1ef1, b\u1ea1n t\u1ea1o ra m\u1ed9t th\u1eb1ng B&#8217; gi\u1ea3 m\u1ea1o, c\u00f3 \u0111\u1ea7y \u0111\u1ee7 ch\u1ee9c n\u0103ng nh\u01b0 B th\u1eadt (nh\u01b0ng kh\u00f4ng ph\u1ea3i th\u1eadt).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">B\u1ea1n s\u1ebd gi\u1ea3 l\u1eadp cho B&#8217; bi\u1ebft l\u00e0 khi th\u1eb1ng A g\u1ecdi t\u1edbi n\u00f3, n\u00f3 c\u1ea7n l\u00e0m g\u00ec, tr\u1ea3 l\u1ea1i c\u00e1i g\u00ec (hardcode). Mi\u1ec5n l\u00e0m sao cho n\u00f3 tr\u1ea3 ra \u0111\u00fang c\u00e1i th\u1eb1ng A c\u1ea7n \u0111\u1ec3 ch\u00fang ta c\u00f3 th\u1ec3 test A thu\u1eadn l\u1ee3i nh\u1ea5t. C\u00f3 nh\u01b0 v\u1eady m\u00f4i tr\u01b0\u1eddng test c\u1ee7a ch\u00fang ta m\u1edbi \u0111\u1ed9c l\u1eadp \u0111\u01b0\u1ee3c v\u1edbi m\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C\u00e1ch m\u00ecnh l\u00e0m gi\u1ea3 CategoryRepositor \u1edf \u0111\u00e2y l\u00e0 m\u00ecnh s\u1eed d\u1ee5ng <code>Mockito.<em>mock<\/em>()<\/code>\u0111\u1ec3 t\u1ea1o ra m\u1ed9t object c\u1ee7a\u00a0<code>Class<\/code>\u00a0b\u1ea1n y\u00eau c\u1ea7u, n\u00f3 th\u1eadm ch\u00ed c\u00f2n kh\u00f4ng quan t\u00e2m h\u00e0m kh\u1edfi t\u1ea1o c\u1ee7a\u00a0<code>Class<\/code>\u00a0\u00fd nh\u01b0 n\u00e0o v\u00e0 ra sao, v\u00ec n\u00f3 \u0111\u00e2u c\u00f3 th\u1eadt. N\u1ebfu c\u00e1c b\u1ea1n mu\u1ed1n d\u00f9ng c\u00e1ch kh\u00e1c c\u00f3 th\u1ec3 t\u00ecm hi\u1ec3u th\u00eam <code>@Mock<\/code> ho\u1eb7c <code>Mock Init<\/code>. Trong CategoryService m\u00ecnh c\u00f2n s\u1eed d\u1ee5ng OutcomeRepository n\u1eefa n\u00ean m\u00ecnh l\u00e0 t\u01b0\u01a1ng t\u1ef1 v\u1edbi CategoryRepository nh\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sau khi l\u00e0m gi\u1ea3 \u0111\u01b0\u1ee3c <code>CategoryRepository<\/code> ch\u00fang ta ti\u00eam n\u00f3 v\u00e0o trong  h\u00e0m kh\u1edfi t\u1ea1o c\u1ee7a CategoryService <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">private ICategoryService categoryService = new CategoryService(categoryRepository);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">M\u00ecnh test ph\u01b0\u01a1ng th\u1ee9c <code>findAll()<\/code> tr\u01b0\u1edbc c\u00e1c b\u1ea1n nh\u00e9. Ch\u00fang ta s\u1eed d\u1ee5ng @Test \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u ph\u01b0\u01a1ng th\u1ee9c Test nh\u00e9.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Test\n@DisplayName(\"findAll can return list is not null\")\npublic void whenFindAllNotNull() {\n         \/\/\/\/\/\/...\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Gi\u1edd m\u00ecnh s\u1ebd t\u1ea1o ra 1 list Category v\u00e0 n\u00f3i cho Repo gi\u1ea3 v\u1eeba t\u1ea1o r\u1eb1ng khi g\u1ecdi ph\u01b0\u01a1ng th\u1ee9c findAll c\u1ee7a Repo s\u1ebd tr\u1ea3 v\u1ec1 list \u0111\u00f3:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u0110o\u1ea1n m\u00e3 n\u00e0y m\u00ecnh t\u1ea1o Outcome gi\u1ea3 \u0111\u1ec3 m\u00ecnh test <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    Outcomes outcomes = new Outcomes();\n    outcomes.setTitle(\"PH\u1ea6N 1: KI\u1ebeN TH\u1ee8C V\u00c0 K\u1ef8 N\u0102NG L\u1eacP TR\u00ccNH\");\n    doReturn(Optional.of(outcomes)).when(outcomesRepository).findById(1L);\n    List&lt;Outcomes> outcomesIterable = Arrays.asList(outcomes);\n    doReturn(outcomesIterable).when(outcomesRepository).findAll();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ph\u1ea7n n\u00e0y m\u00ecnh x\u00e1c \u0111\u1ecbnh d\u00f9ng l\u1ea1i nhi\u1ec1u l\u1ea7n \u1edf c\u00e1c Test n\u00ean m\u00ecnh s\u1eed \u0111\u1eb7t n\u00f3 trong  <code>@BeforeEach<\/code> ngh\u0129a l\u00e0 nh\u1eefng l\u1ec7nh ch\u1ea1y trong ph\u01b0\u01a1ng th\u1ee9c n\u00e0y s\u1ebd \u0111\u01b0\u1ee3c ch\u1ea1y l\u1ea1i tr\u01b0\u1edbc m\u1ed7i Test.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> Categories category = new Categories();\n category.setCategoryId(\"1.1\");\n category.setName(\"L\u1eadp tr\u00ecnh c\u01a1 b\u1ea3n\");\n doReturn(Optional.of(category)).when(categoryRepository).findById(1L);\n doReturn(Optional.of(category)).when(categoryRepository).findByName(\"L\u1eadp tr\u00ecnh c\u01a1 b\u1ea3n\");\n List&lt;Categories> categoriesList = Arrays.asList(category);\n doReturn(categoriesList).when(categoryRepository).findAll();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ph\u01b0\u01a1ng th\u1ee9c findAll() m\u00ecnh test 2 vi\u1ec7c, 1 l\u00e0 n\u00f3 tr\u1ea3 v\u1ec1 1 list kh\u00f4ng ph\u1ea3i list tr\u1ed1ng, 2 l\u00e0 k\u00edch th\u01b0\u1edbc c\u1ee7a list \u0111\u01b0\u1ee3c tr\u1ea3 v\u1ec1 l\u00e0 1, n\u1ebfu c\u00e1c b\u1ea1n mu\u1ed1n c\u00f3 th\u1ec3 thay \u0111\u1ed5i k\u00edch th\u01b0\u1edbc n\u00e0y trong ph\u1ea7n chu\u1ea9n b\u1ecb d\u1eef li\u1ec7u. M\u00ecnh h\u01a1i l\u01b0\u1eddi n\u00ean th\u1ebf th\u00f4i v\u1eady. \u1ede \u0111\u00e2y m\u00ecnh ph\u1ea3i vi\u1ebft 2 ph\u01b0\u01a1ng th\u1ee9c Test c\u00e1c b\u1ea1n nh\u00e9.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Test\n    @DisplayName(\"findAll can return list is not null\")\n    public void whenFindAllNotNull() {\n        assertThat(categoryService.findAll()).isNotNull();\n    }\n\n    @Test\n    @DisplayName(\"findAll can return a list has 1 element\")\n    public void whenfindAll_thenReturnListHasOneElement() {\n        Iterable&lt;Categories> categories = categoryService.findAll();\n        assertThat(categories).hasSize(1);\n    }\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ti\u1ebfp t\u1ee5c m\u00ecnh test ph\u01b0\u01a1ng th\u1ee9c findById, trong ph\u1ea7n chu\u1ea9n b\u1ecb d\u1eef li\u1ec7u test m\u00ecnh b\u00e1o cho repo bi\u1ebft l\u00e0 khi g\u1ecdi ph\u01b0\u01a1ng th\u1ee9c findById v\u1edbi tham s\u1ed1 1L n\u00f3 s\u1ebd tr\u1ea3 v\u1ec1 category n\u00e0o? nh\u01b0 sau:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>doReturn(Optional.of(category)).when(categoryRepository).findById(1L);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">V\u1edbi ph\u01b0\u01a1ng th\u1ee9c n\u00e0y m\u00ecnh test 2 vi\u1ec7c, 1 l\u00e0 m\u00ecnh test tr\u01b0\u1eddng h\u1ee3p t\u00ecm th\u1ea5y category t\u01b0\u01a1ng \u1ee9ng khi truy\u1ec1n tham s\u1ed1 1L, 2 l\u00e0 m\u00ecnh test tr\u01b0\u1eddng h\u1ee3p kh\u00f4ng t\u00ecm th\u1ea5y category khi truy\u1ec1n tham s\u1ed1 2L.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Test\n    @DisplayName(\"findbyID return category name Lap trinh co ban\")\n    public void whenfindById_thenReturnCategory() {\n        String name = \"L\u1eadp tr\u00ecnh c\u01a1 b\u1ea3n\";\n        Optional&lt;Categories> categories = categoryService.findById(1L);\n        assertThat(categories.get().getName()).isEqualTo(name);\n    }\n\n    @Test\n    @DisplayName(\"findbyID 2L return isPresent\")\n    public void whenfindById_thenReturnCategoryisPresent() {\n        Optional&lt;Categories> categories = categoryService.findById(2L);\n        assertThat(categories.isPresent()).isFalse();\n    }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">T\u01b0\u01a1ng t\u1ef1 v\u1edbi c\u00e1c ph\u01b0\u01a1ng th\u1ee9c kh\u00e1c, c\u00e1c b\u1ea1n c\u00f3 th\u1ec3  tham kh\u1ea3o m\u00e3 test t\u1ea1i <a href=\"https:\/\/github.com\/VuKieuAnh\/ePortfolio\/blob\/dev\/src\/test\/java\/com\/example\/portfolio\/service\/categories\/CategoryServiceTest.java\">\u0111\u00e2y<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">V\u1eady l\u00e0 m\u00ecnh \u0111\u00e3 h\u01b0\u1edbng d\u1eabn c\u00e1c b\u1ea1n vi\u1ebft Test v\u1edbi module Service trong Spring Boot. B\u00e0i sau m\u00ecnh s\u1ebd h\u01b0\u1edbng d\u1eabn c\u00e1c b\u1ea1n vi\u1ebft Test Controller c\u00e1c b\u1ea1n nh\u00e9. &lt;3<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Trong b\u00e0i vi\u1ebft n\u00e0y m\u00ecnh s\u1ebd h\u01b0\u1edbng d\u1eabn c\u00e1c b\u1ea1n vi\u1ebft Unit Test t\u1ea7ng Service c\u1ee7a Spring Boot. Trong Spring \u0111\u1ec3 s\u1eed d\u1ee5ng \u0111\u01b0\u1ee3c m\u00f4i tr\u01b0\u1eddng test ta c\u1ea7n cung c\u1ea5p c\u00e1c th\u01b0 vi\u1ec7n H2 l\u00e0 m\u1ed9t c\u01a1 s\u1edf d\u1eef li\u1ec7u quan h\u1ec7 (Relational database) \u0111\u01b0\u1ee3c ph\u00e1t tri\u1ec3n b\u1edfi c\u00f4ng ty H2 Software. H2 \u0111\u01b0\u1ee3c [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-21","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/posts\/21","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/comments?post=21"}],"version-history":[{"count":11,"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/posts\/21\/revisions"}],"predecessor-version":[{"id":194,"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/posts\/21\/revisions\/194"}],"wp:attachment":[{"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/media?parent=21"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/categories?post=21"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vukieuanh.net\/index.php\/wp-json\/wp\/v2\/tags?post=21"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}