Skip to content
This repository was archived by the owner on Mar 19, 2025. It is now read-only.

Commit 39b1998

Browse files
authored
作成済み記事の削除APIのテスト (#276)
* test: Articleのdelete apiの正常系のテストを記述 * test: 自分が著者ではない作成済み記事の削除に失敗するテストを記述 * test: 存在しないSlugを指定して作成済み記事の削除に失敗するテストを記述 * test: 登録済み記事の削除時に、バリデーションエラーが起きる場合のテストを記述
1 parent 86f4425 commit 39b1998

File tree

2 files changed

+283
-10
lines changed

2 files changed

+283
-10
lines changed

src/test/kotlin/com/example/realworldkotlinspringbootjdbc/api_integration/ArticleTest.kt

+209-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import com.github.database.rider.core.api.dataset.ExpectedDataSet
1212
import com.github.database.rider.junit5.api.DBRider
1313
import org.assertj.core.api.Assertions.assertThat
1414
import org.junit.jupiter.api.BeforeEach
15-
import org.junit.jupiter.api.Disabled
1615
import org.junit.jupiter.api.Test
1716
import org.junit.jupiter.api.TestInstance
1817
import org.skyscreamer.jsonassert.Customization
@@ -22,8 +21,10 @@ import org.skyscreamer.jsonassert.comparator.CustomComparator
2221
import org.springframework.beans.factory.annotation.Autowired
2322
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
2423
import org.springframework.boot.test.context.SpringBootTest
24+
import org.springframework.http.HttpStatus
2525
import org.springframework.http.MediaType
2626
import org.springframework.test.web.servlet.MockMvc
27+
import org.springframework.test.web.servlet.delete
2728
import org.springframework.test.web.servlet.get
2829
import org.springframework.test.web.servlet.post
2930
import org.springframework.util.MultiValueMapAdapter
@@ -1761,28 +1762,226 @@ class ArticleTest {
17611762
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
17621763
@DBRider
17631764
class DeleteArticle {
1764-
@Disabled
1765+
1766+
@Autowired
1767+
lateinit var mockMvc: MockMvc
1768+
1769+
@BeforeEach
1770+
fun reset() = DbConnection.resetSequence()
1771+
17651772
@Test
1773+
@DataSet(
1774+
value = [
1775+
"datasets/yml/given/users.yml",
1776+
"datasets/yml/given/tags.yml",
1777+
"datasets/yml/given/articles.yml",
1778+
]
1779+
)
1780+
@ExpectedDataSet(
1781+
value = ["datasets/yml/then/api_integration/delete-success.yml"],
1782+
ignoreCols = ["id", "created_at", "updated_at"],
1783+
orderBy = ["id"]
1784+
)
1785+
// NOTE: @ExportDataSetはgivenの@DataSetが変更された時用に残しておく
1786+
// @ExportDataSet(
1787+
// format = DataSetFormat.YML,
1788+
// outputName = "src/test/resources/datasets/yml/then/api_integration/delete-success.yml",
1789+
// includeTables = ["articles", "tags", "article_tags", "favorites", "article_comments"]
1790+
// )
17661791
fun `正常系-自分が著者である記事のSlugを指定した場合、その作成済み記事を削除する`() {
1767-
TODO()
1792+
/**
1793+
* given:
1794+
* - 存在する著者
1795+
* - 著者が書いた作成済み記事のslug
1796+
*/
1797+
val author = SeedData.users().first()
1798+
val sessionToken = MySessionJwtImpl.encode(MySession(author.userId, author.email))
1799+
.getOrHandle { throw UnsupportedOperationException("セッションからJWTへの変換に失敗しました(前提条件であるため、元の実装を見直してください)") }
1800+
val slug = "rust-vs-scala-vs-kotlin"
1801+
1802+
/**
1803+
* when:
1804+
*/
1805+
val response = mockMvc.delete("/articles/$slug") {
1806+
contentType = MediaType.APPLICATION_JSON
1807+
header("Authorization", sessionToken)
1808+
}.andReturn().response
1809+
val actualStatus = response.status
1810+
val actualResponseBody = response.contentAsString
1811+
1812+
/**
1813+
* then:
1814+
*/
1815+
val expectedStatus = HttpStatus.OK.value()
1816+
val expectedResponseBody = ""
1817+
assertThat(actualStatus).isEqualTo(expectedStatus)
1818+
assertThat(actualResponseBody).isEqualTo(expectedResponseBody)
17681819
}
17691820

1770-
@Disabled
17711821
@Test
1822+
@DataSet(
1823+
value = [
1824+
"datasets/yml/given/users.yml",
1825+
"datasets/yml/given/tags.yml",
1826+
"datasets/yml/given/articles.yml",
1827+
]
1828+
)
1829+
@ExpectedDataSet(
1830+
value = [
1831+
"datasets/yml/given/tags.yml",
1832+
"datasets/yml/given/articles.yml",
1833+
],
1834+
ignoreCols = ["id", "created_at", "updated_at"],
1835+
orderBy = ["id"]
1836+
)
17721837
fun `準正常系-自分が著者ではない記事のSlugを指定した場合、その作成済み記事は削除できない`() {
1773-
TODO()
1838+
/**
1839+
* given:
1840+
* - 著者ではない存在する登録済みユーザー
1841+
* - 登録済み記事のslug
1842+
*/
1843+
val author = SeedData.users().toList()[1]
1844+
val sessionToken = MySessionJwtImpl.encode(MySession(author.userId, author.email))
1845+
.getOrHandle { throw UnsupportedOperationException("セッションからJWTへの変換に失敗しました(前提条件であるため、元の実装を見直してください)") }
1846+
val slug = "rust-vs-scala-vs-kotlin"
1847+
1848+
/**
1849+
* when:
1850+
*/
1851+
val response = mockMvc.delete("/articles/$slug") {
1852+
contentType = MediaType.APPLICATION_JSON
1853+
header("Authorization", sessionToken)
1854+
}.andReturn().response
1855+
val actualStatus = response.status
1856+
val actualResponseBody = response.contentAsString
1857+
1858+
/**
1859+
* then:
1860+
*/
1861+
val expectedStatus = HttpStatus.UNPROCESSABLE_ENTITY.value()
1862+
val expectedResponseBody = """
1863+
{"errors":{"body":["削除する権限がありません"]}}
1864+
""".trimIndent()
1865+
assertThat(actualStatus).isEqualTo(expectedStatus)
1866+
JSONAssert.assertEquals(
1867+
expectedResponseBody,
1868+
actualResponseBody,
1869+
CustomComparator(JSONCompareMode.NON_EXTENSIBLE)
1870+
)
17741871
}
17751872

1776-
@Disabled
17771873
@Test
1874+
@DataSet(
1875+
value = [
1876+
"datasets/yml/given/users.yml",
1877+
"datasets/yml/given/tags.yml",
1878+
"datasets/yml/given/articles.yml",
1879+
]
1880+
)
1881+
@ExpectedDataSet(
1882+
value = [
1883+
"datasets/yml/given/tags.yml",
1884+
"datasets/yml/given/articles.yml",
1885+
],
1886+
ignoreCols = ["id", "created_at", "updated_at"],
1887+
orderBy = ["id"]
1888+
)
17781889
fun `準正常系-存在しないSlugを指定した場合、その作成済み記事は見つからなかった旨のエラーレスポンスが返る`() {
1779-
TODO()
1890+
/**
1891+
* given:
1892+
* - 存在する登録済みユーザー
1893+
* - 存在しないslug
1894+
*/
1895+
val existedUser = SeedData.users().first()
1896+
val sessionToken = MySessionJwtImpl.encode(MySession(existedUser.userId, existedUser.email))
1897+
.getOrHandle { throw UnsupportedOperationException("セッションからJWTへの変換に失敗しました(前提条件であるため、元の実装を見直してください)") }
1898+
val slug = "not-existed-slug"
1899+
1900+
/**
1901+
* when:
1902+
*/
1903+
val response = mockMvc.delete("/articles/$slug") {
1904+
contentType = MediaType.APPLICATION_JSON
1905+
header("Authorization", sessionToken)
1906+
}.andReturn().response
1907+
val actualStatus = response.status
1908+
val actualResponseBody = response.contentAsString
1909+
1910+
/**
1911+
* then:
1912+
*/
1913+
val expectedStatus = HttpStatus.UNPROCESSABLE_ENTITY.value()
1914+
val expectedResponseBody = """
1915+
{"errors":{"body":["記事が見つかりませんでした"]}}
1916+
""".trimIndent()
1917+
assertThat(actualStatus).isEqualTo(expectedStatus)
1918+
JSONAssert.assertEquals(
1919+
expectedResponseBody,
1920+
actualResponseBody,
1921+
CustomComparator(JSONCompareMode.NON_EXTENSIBLE)
1922+
)
17801923
}
17811924

1782-
@Disabled
17831925
@Test
1784-
fun `準正常系-バリデーションエラーが起こるSlugを指定した場合、その作成済み記事は見つからなかった旨のエラーレスポンスが返る`() {
1785-
TODO()
1926+
@DataSet(
1927+
value = [
1928+
"datasets/yml/given/users.yml",
1929+
"datasets/yml/given/tags.yml",
1930+
"datasets/yml/given/articles.yml",
1931+
]
1932+
)
1933+
@ExpectedDataSet(
1934+
value = [
1935+
"datasets/yml/given/tags.yml",
1936+
"datasets/yml/given/articles.yml",
1937+
],
1938+
ignoreCols = ["id", "created_at", "updated_at"],
1939+
orderBy = ["id"]
1940+
)
1941+
fun `準正常系-バリデーションエラーが起こるSlugを指定した場合、その旨のエラーレスポンスが返る`() {
1942+
/**
1943+
* given:
1944+
* - 存在する登録済みユーザー
1945+
* - バリデーションエラーが起こるslug
1946+
*/
1947+
val existedUser = SeedData.users().first()
1948+
val sessionToken = MySessionJwtImpl.encode(MySession(existedUser.userId, existedUser.email))
1949+
.getOrHandle { throw UnsupportedOperationException("セッションからJWTへの変換に失敗しました(前提条件であるため、元の実装を見直してください)") }
1950+
val slug = IntStream.range(0, 10).mapToObj { "長すぎるslug" }.toList().joinToString()
1951+
1952+
/**
1953+
* when:
1954+
*/
1955+
val response = mockMvc.delete("/articles/$slug") {
1956+
contentType = MediaType.APPLICATION_JSON
1957+
header("Authorization", sessionToken)
1958+
}.andReturn().response
1959+
val actualStatus = response.status
1960+
val actualResponseBody = response.contentAsString
1961+
1962+
/**
1963+
* then:
1964+
*/
1965+
val expectedStatus = HttpStatus.UNPROCESSABLE_ENTITY.value()
1966+
val expectedResponseBody = """
1967+
{
1968+
"errors":{
1969+
"body":[
1970+
{
1971+
"slug": "長すぎるslug, 長すぎるslug, 長すぎるslug, 長すぎるslug, 長すぎるslug, 長すぎるslug, 長すぎるslug, 長すぎるslug, 長すぎるslug, 長すぎるslug",
1972+
"key":"Slug",
1973+
"message":"slugは32文字以下にしてください。"
1974+
}
1975+
]
1976+
}
1977+
}
1978+
""".trimIndent()
1979+
assertThat(actualStatus).isEqualTo(expectedStatus)
1980+
JSONAssert.assertEquals(
1981+
expectedResponseBody,
1982+
actualResponseBody,
1983+
CustomComparator(JSONCompareMode.NON_EXTENSIBLE)
1984+
)
17861985
}
17871986
}
17881987
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
favorites:
2+
- id: 2
3+
user_id: 3
4+
article_id: 2
5+
created_at: "2022-10-26 21:02:48.0"
6+
- id: 3
7+
user_id: 1
8+
article_id: 3
9+
created_at: "2022-10-26 21:02:48.0"
10+
- id: 4
11+
user_id: 2
12+
article_id: 3
13+
created_at: "2022-10-26 21:02:48.0"
14+
15+
article_comments:
16+
- id: 2
17+
author_id: 1
18+
article_id: 3
19+
body: "dummy-comment-body-02"
20+
created_at: "2022-10-26 21:02:48.0"
21+
updated_at: "2022-10-26 21:02:48.0"
22+
- id: 4
23+
author_id: 3
24+
article_id: 3
25+
body: "dummy-comment-body-04"
26+
created_at: "2022-10-26 21:02:48.0"
27+
updated_at: "2022-10-26 21:02:48.0"
28+
29+
articles:
30+
- id: 2
31+
author_id: 1
32+
title: "Functional programming kotlin"
33+
slug: "functional-programming-kotlin"
34+
body: "dummy-body"
35+
description: "dummy-description"
36+
created_at: "2022-01-01 00:00:00.0"
37+
updated_at: "2022-01-02 00:00:00.0"
38+
- id: 3
39+
author_id: 2
40+
title: "TDD(Type Driven Development)"
41+
slug: "tdd-type-driven-development"
42+
body: "dummy-body"
43+
description: "dummy-description"
44+
created_at: "2022-01-01 00:00:00.0"
45+
updated_at: "2022-01-03 00:00:00.0"
46+
47+
article_tags:
48+
- id: 4
49+
article_id: 2
50+
tag_id: 3
51+
created_at: "2022-01-01 00:00:00.0"
52+
53+
tags:
54+
- id: 1
55+
name: "rust"
56+
created_at: "2022-10-26 21:02:48.0"
57+
updated_at: "2022-10-26 21:02:48.0"
58+
- id: 2
59+
name: "scala"
60+
created_at: "2022-10-26 21:02:48.0"
61+
updated_at: "2022-10-26 21:02:48.0"
62+
- id: 3
63+
name: "kotlin"
64+
created_at: "2022-10-26 21:02:48.0"
65+
updated_at: "2022-10-26 21:02:48.0"
66+
- id: 4
67+
name: "ocaml"
68+
created_at: "2022-10-26 21:02:48.0"
69+
updated_at: "2022-10-26 21:02:48.0"
70+
- id: 5
71+
name: "elixir"
72+
created_at: "2022-10-26 21:02:48.0"
73+
updated_at: "2022-10-26 21:02:48.0"
74+

0 commit comments

Comments
 (0)