Skip to content

Commit e52491e

Browse files
committed
test: Article, 게시글 등록 인수테스트 작성
1 parent 8d43967 commit e52491e

File tree

11 files changed

+172
-38
lines changed

11 files changed

+172
-38
lines changed

src/main/java/com/study/realworld/domain/article/api/ArticleCommandApi.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.study.realworld.domain.article.api;
22

33
import com.study.realworld.domain.article.application.ArticleCommandService;
4+
import com.study.realworld.domain.article.domain.persist.Article;
45
import com.study.realworld.domain.article.dto.ArticleSave;
6+
import com.study.realworld.domain.article.strategy.SlugStrategy;
57
import lombok.RequiredArgsConstructor;
68
import org.springframework.http.ResponseEntity;
79
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@@ -16,11 +18,13 @@
1618
public class ArticleCommandApi {
1719

1820
private final ArticleCommandService articleCommandService;
21+
private final SlugStrategy slugStrategy;
1922

2023
@PostMapping("/articles")
21-
public ResponseEntity<?> save(@AuthenticationPrincipal final Long userId,
22-
@Valid @RequestBody ArticleSave.Request request) {
23-
final ArticleSave.Response response = articleCommandService.save(userId, request.toEntity());
24+
public ResponseEntity<ArticleSave.Response> save(@AuthenticationPrincipal final Long userId,
25+
@Valid @RequestBody final ArticleSave.Request request) {
26+
final Article article = request.toEntity(slugStrategy);
27+
final ArticleSave.Response response = articleCommandService.save(userId, article);
2428
return ResponseEntity.ok().body(response);
2529
}
2630
}

src/main/java/com/study/realworld/domain/article/application/ArticleCommandService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class ArticleCommandService {
2020
public ArticleSave.Response save(final Long userId, final Article article) {
2121
final User user = userQueryService.findById(userId);
2222
article.changeAuthor(user);
23-
return ArticleSave.Response.from(articleRepository.save(article));
23+
final Article savedArticle = articleRepository.save(article);
24+
return ArticleSave.Response.from(savedArticle);
2425
}
2526
}

src/main/java/com/study/realworld/domain/article/domain/persist/Article.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public User author() {
7373
return author;
7474
}
7575

76-
public Article changeAuthor(final User user) {
76+
public Article changeAuthor(final User author) {
7777
this.author = author;
7878
return this;
7979
}

src/main/java/com/study/realworld/domain/article/dto/ArticleSave.java

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.study.realworld.domain.article.dto;
22

33
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
5+
import com.fasterxml.jackson.annotation.JsonTypeName;
46
import com.study.realworld.domain.article.domain.persist.Article;
57
import com.study.realworld.domain.article.domain.vo.ArticleBody;
68
import com.study.realworld.domain.article.domain.vo.ArticleDescription;
79
import com.study.realworld.domain.article.domain.vo.ArticleSlug;
810
import com.study.realworld.domain.article.domain.vo.ArticleTitle;
11+
import com.study.realworld.domain.article.strategy.SlugStrategy;
912
import com.study.realworld.domain.user.domain.persist.User;
1013
import com.study.realworld.domain.user.domain.vo.UserBio;
1114
import com.study.realworld.domain.user.domain.vo.UserImage;
@@ -20,31 +23,40 @@
2023

2124
public class ArticleSave {
2225

23-
@Builder
26+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
27+
@JsonTypeName("article")
28+
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
2429
public static class Request {
2530

2631
@JsonProperty("title")
27-
private String title;
32+
private ArticleTitle articleTitle;
2833

2934
@JsonProperty("description")
30-
private String description;
35+
private ArticleDescription articleDescription;
3136

3237
@JsonProperty("body")
33-
private String body;
38+
private ArticleBody articleBody;
3439

3540
@JsonProperty("tagList")
3641
private List<String> tags;
3742

3843
@Builder
39-
public Request(final String title, final String description, final String body, final List<String> tags) {
40-
this.title = title;
41-
this.description = description;
42-
this.body = body;
44+
public Request(final ArticleTitle articleTitle, final ArticleDescription articleDescription,
45+
final ArticleBody articleBody, final List<String> tags) {
46+
this.articleTitle = articleTitle;
47+
this.articleDescription = articleDescription;
48+
this.articleBody = articleBody;
4349
this.tags = tags;
4450
}
4551

46-
public Article toEntity() {
47-
return null;
52+
public Article toEntity(final SlugStrategy slugStrategy) {
53+
final String articleSlug = slugStrategy.mapToSlug(articleTitle.articleTitle());
54+
return Article.builder()
55+
.articleSlug(ArticleSlug.from(articleSlug))
56+
.articleTitle(articleTitle)
57+
.articleDescription(articleDescription)
58+
.articleBody(articleBody)
59+
.build();
4860
}
4961
}
5062

@@ -93,24 +105,24 @@ public static Response from(final Article article) {
93105

94106
return new Response(
95107
articleSlug, articleTitle, articleDescription, articleBody,
96-
createdAt, updatedAt, List.of("dragons", "training"),
108+
createdAt, updatedAt, List.of("reactjs", "angularjs", "dragons"),
97109
false, 0, authorDto
98110
);
99111
}
100112

101-
public ArticleSlug slug() {
113+
public ArticleSlug articleSlug() {
102114
return articleSlug;
103115
}
104116

105-
public ArticleTitle title() {
117+
public ArticleTitle articleTitle() {
106118
return articleTitle;
107119
}
108120

109-
public ArticleDescription description() {
121+
public ArticleDescription articleDescription() {
110122
return articleDescription;
111123
}
112124

113-
public ArticleBody body() {
125+
public ArticleBody articleBody() {
114126
return articleBody;
115127
}
116128

@@ -141,9 +153,17 @@ public AuthorDto author() {
141153
@AllArgsConstructor(access = AccessLevel.PUBLIC)
142154
@NoArgsConstructor(access = AccessLevel.PRIVATE)
143155
public static class AuthorDto {
156+
157+
@JsonProperty("username")
144158
private UserName userName;
159+
160+
@JsonProperty("bio")
145161
private UserBio userBio;
162+
163+
@JsonProperty("image")
146164
private UserImage userImage;
165+
166+
@JsonProperty("following")
147167
private boolean following;
148168

149169
public static AuthorDto from(final User author) {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.study.realworld.domain.article.strategy;
2+
3+
public class SimpleDashBarSlugStrategy implements SlugStrategy {
4+
private static final String FIRST_REGEX = "\\$,'\"|\\s|\\.|\\?";
5+
private static final String SECOND_REGEX = "-{2,}";
6+
private static final String THIRD_REGEX = "(^-)|(-$)";
7+
private static final String REPLACEMENT = "-";
8+
private static final String EMPTY = "";
9+
10+
@Override
11+
public String mapToSlug(final String url) {
12+
return url.toLowerCase()
13+
.replaceAll(FIRST_REGEX, REPLACEMENT)
14+
.replaceAll(SECOND_REGEX, REPLACEMENT)
15+
.replaceAll(THIRD_REGEX, EMPTY);
16+
}
17+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.study.realworld.domain.article.strategy;
2+
3+
@FunctionalInterface
4+
public interface SlugStrategy {
5+
String mapToSlug(final String url);
6+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.study.realworld.global.config;
2+
3+
import com.fasterxml.jackson.databind.DeserializationFeature;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.fasterxml.jackson.databind.SerializationFeature;
6+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
7+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
11+
@Configuration
12+
public class JacksonConfiguration {
13+
14+
@Bean
15+
public ObjectMapper objectMapper() {
16+
ObjectMapper mapper = new ObjectMapper();
17+
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
18+
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
19+
mapper.registerModules(new JavaTimeModule(), new Jdk8Module());
20+
return mapper;
21+
}
22+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.study.realworld.global.config;
2+
3+
import com.study.realworld.domain.article.strategy.SimpleDashBarSlugStrategy;
4+
import com.study.realworld.domain.article.strategy.SlugStrategy;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
8+
@Configuration
9+
public class UtilBeanConfiguration {
10+
11+
@Bean
12+
public SlugStrategy slugStrategy() {
13+
return new SimpleDashBarSlugStrategy();
14+
}
15+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
### JOIN
2+
POST localhost:8080/api/users
3+
Content-Type: application/json
4+
5+
{
6+
"user": {
7+
"username": "woojiji",
8+
"email": "woojiji@jake.jake",
9+
"password": "woojijiwoojiji"
10+
}
11+
}
12+
13+
### LOGIN
14+
POST localhost:8080/api/users/login
15+
Content-Type: application/json
16+
17+
{
18+
"user": {
19+
"email": "woojiji@jake.jake",
20+
"password": "woojijiwoojiji"
21+
}
22+
}
23+
24+
> {%
25+
client.global.set("Authorization", response.body.user.token);
26+
client.log("생성된 Authorization : " + client.global.get("Authorization"));
27+
%}
28+
29+
### ARTICLE_SAVE
30+
POST localhost:8080/api/articles
31+
Content-Type: application/json
32+
Authorization: {{Authorization}}
33+
34+
{
35+
"article": {
36+
"title": "How to train your dragon",
37+
"description": "Ever wonder how?",
38+
"body": "You have to believe",
39+
"tagList": [
40+
"reactjs",
41+
"angularjs",
42+
"dragons"
43+
]
44+
}
45+
}

src/test/java/com/study/realworld/acceptance/ArticleAcceptanceTest.java

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.study.realworld.acceptance;
22

3+
import com.study.realworld.domain.article.domain.vo.ArticleBody;
4+
import com.study.realworld.domain.article.domain.vo.ArticleDescription;
5+
import com.study.realworld.domain.article.domain.vo.ArticleTitle;
36
import com.study.realworld.domain.article.dto.ArticleSave;
47
import com.study.realworld.domain.auth.dto.Login;
58
import com.study.realworld.domain.user.dto.UserJoin;
@@ -42,25 +45,26 @@ public void setUp() {
4245

4346
assertAll(
4447
() -> assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()),
45-
() -> assertThat(articleResponse.slug()).isEqualTo("how-to-train-your-dragon"),
46-
() -> assertThat(articleResponse.title()).isEqualTo("how to train your dragon"),
47-
() -> assertThat(articleResponse.description()).isEqualTo("Ever wonder how?"),
48-
() -> assertThat(articleResponse.body()).isEqualTo("It takes a Jacobian"),
49-
() -> assertThat(articleResponse.tags()).isEqualTo(List.of("dragons", "training")),
50-
() -> assertThat(articleResponse.createdAt()).isEqualTo("2016-02-18T03:22:56.637Z"),
51-
() -> assertThat(articleResponse.updatedAt()).isEqualTo("2016-02-18T03:48:35.824Z"),
48+
() -> assertThat(articleResponse.articleSlug().articleSlug()).isEqualTo("how-to-train-your-dragon"),
49+
() -> assertThat(articleResponse.articleTitle().articleTitle()).isEqualTo("How to train your dragon"),
50+
() -> assertThat(articleResponse.articleDescription().articleDescription()).isEqualTo("Ever wonder how?"),
51+
() -> assertThat(articleResponse.articleBody().articleBody()).isEqualTo("You have to believe"),
52+
() -> assertThat(articleResponse.tags()).isEqualTo(List.of("reactjs", "angularjs", "dragons")),
53+
() -> assertThat(articleResponse.createdAt()).isNotNull(),
54+
() -> assertThat(articleResponse.updatedAt()).isNotNull(),
5255
() -> assertThat(articleResponse.favorited()).isEqualTo(false),
5356
() -> assertThat(articleResponse.favoritesCount()).isEqualTo(0),
54-
() -> assertThat(articleResponse.author().userName()).isEqualTo("jake"),
55-
() -> assertThat(articleResponse.author().userBio()).isEqualTo("I work at statefarm"),
56-
() -> assertThat(articleResponse.author().userImage()).isEqualTo("https://i.stack.imgur.com/xHWG8.jpg"),
57+
() -> assertThat(articleResponse.author().userName().userName()).isEqualTo("woozi"),
58+
() -> assertThat(articleResponse.author().userBio()).isNull(),
59+
() -> assertThat(articleResponse.author().userImage()).isNull(),
5760
() -> assertThat(articleResponse.author().following()).isEqualTo(false)
5861
);
5962
}
6063

6164
protected ExtractableResponse<Response> 정상적인_게시글_등록_요청(final AccessToken accessToken) {
6265
final ArticleSave.Request request = 정상적인_게시글_정보();
6366
return RestAssured.given()
67+
.header(AUTHORIZATION, BEARER + accessToken.accessToken())
6468
.contentType(MediaType.APPLICATION_JSON_VALUE)
6569
.body(request)
6670
.when()
@@ -71,9 +75,9 @@ public void setUp() {
7175

7276
protected ArticleSave.Request 정상적인_게시글_정보() {
7377
return ArticleSave.Request.builder()
74-
.title("How to train your dragon")
75-
.description("Ever wonder how?")
76-
.body("You have to believe")
78+
.articleTitle(ArticleTitle.from("How to train your dragon"))
79+
.articleDescription(ArticleDescription.from("Ever wonder how?"))
80+
.articleBody(ArticleBody.from("You have to believe"))
7781
.tags(List.of("reactjs", "angularjs", "dragons"))
7882
.build();
7983
}

src/test/java/com/study/realworld/domain/article/application/ArticleCommandServiceTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ class ArticleCommandServiceTest {
5151

5252
final ArticleSave.Response response = articleCommandService.save(1L, article);
5353
assertAll(
54-
() -> assertThat(response.slug()).isEqualTo(article.articleSlug()),
55-
() -> assertThat(response.title()).isEqualTo(article.articleTitle()),
56-
() -> assertThat(response.body()).isEqualTo(article.articleBody()),
57-
() -> assertThat(response.description()).isEqualTo(article.articleDescription()),
54+
() -> assertThat(response.articleSlug()).isEqualTo(article.articleSlug()),
55+
() -> assertThat(response.articleTitle()).isEqualTo(article.articleTitle()),
56+
() -> assertThat(response.articleBody()).isEqualTo(article.articleBody()),
57+
() -> assertThat(response.articleDescription()).isEqualTo(article.articleDescription()),
5858
() -> assertThat(response.createdAt()).isNotNull(),
5959
() -> assertThat(response.updatedAt()).isNotNull(),
6060
() -> assertThat(response.favorited()).isFalse(),
6161
() -> assertThat(response.favoritesCount()).isZero(),
62-
() -> assertThat(response.tags()).isEqualTo(List.of("dragons", "training")),
62+
() -> assertThat(response.tags()).isEqualTo(List.of("reactjs", "angularjs", "dragons")),
6363
() -> assertThat(response.author().userName()).isEqualTo(user.userName()),
6464
() -> assertThat(response.author().userBio()).isEqualTo(user.userBio()),
6565
() -> assertThat(response.author().userImage()).isEqualTo(user.userImage()),

0 commit comments

Comments
 (0)