diff --git a/demo/src/main/java/org/sopt/demo/DemoApplication.java b/demo/src/main/java/org/sopt/demo/DemoApplication.java new file mode 100644 index 0000000..f66cf6d --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/DemoApplication.java @@ -0,0 +1,13 @@ +package org.sopt.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/demo/src/main/java/org/sopt/demo/common/GlobalExceptionHandler.java b/demo/src/main/java/org/sopt/demo/common/GlobalExceptionHandler.java new file mode 100644 index 0000000..7ccdf70 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/common/GlobalExceptionHandler.java @@ -0,0 +1,24 @@ +package org.sopt.demo.common; + +import org.sopt.demo.common.dto.ErrorResponse; +import org.sopt.demo.exception.NotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Objects; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + protected ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e){ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ErrorResponse.of(HttpStatus.BAD_REQUEST.value(), Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage())); + } + @ExceptionHandler(NotFoundException.class) + protected ResponseEntity handleNotFoundException(NotFoundException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse.of(e.getErrorMessage())); + } +} diff --git a/demo/src/main/java/org/sopt/demo/controller/BlogController.java b/demo/src/main/java/org/sopt/demo/controller/BlogController.java new file mode 100644 index 0000000..4fdfbc9 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/controller/BlogController.java @@ -0,0 +1,41 @@ +package org.sopt.demo.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.sopt.demo.common.dto.SuccessMessage; +import org.sopt.demo.common.dto.SuccessStatusResponse; +import org.sopt.demo.service.BlogService; +import org.sopt.demo.service.dto.BlogCreateRequest; +import org.sopt.demo.service.dto.BlogTitleUpdateRequest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1") +@RequiredArgsConstructor +public class BlogController { + + private final BlogService blogService; + + @PostMapping("/blog") + public ResponseEntity createBlog( + @RequestHeader(name = "memberId") Long memberId, + @RequestBody BlogCreateRequest blogCreateRequest + ) { + return ResponseEntity.status(HttpStatus.CREATED).header( + "Location", + blogService.create(memberId, blogCreateRequest)) + .body(SuccessStatusResponse.of(SuccessMessage.BLOG_CREATE_SUCCESS)); + } + + @PatchMapping("/blog/{blogId}/title") + public ResponseEntity updateBlogTitle( + @PathVariable Long blogId, + @Valid @RequestBody BlogTitleUpdateRequest blogTitleUpdateRequest + ) { + blogService.updateTitle(blogId, blogTitleUpdateRequest); + return ResponseEntity.noContent().build(); + } +} + diff --git a/demo/src/main/java/org/sopt/demo/controller/MemberController.java b/demo/src/main/java/org/sopt/demo/controller/MemberController.java new file mode 100644 index 0000000..21960dd --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/controller/MemberController.java @@ -0,0 +1,52 @@ +package org.sopt.demo.controller; + +import lombok.RequiredArgsConstructor; + +import org.sopt.demo.service.MemberService; +import org.sopt.demo.service.dto.MemberCreateDto; +import org.sopt.demo.service.dto.MemberFindDto; +import org.springframework.http.ResponseEntity; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.net.URI; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/member") +public class MemberController { + + private final MemberService memberService; + + @PostMapping + public ResponseEntity createMember( + @RequestBody MemberCreateDto memberCreateDto + ) { + return ResponseEntity.created(URI.create(memberService.createMember(memberCreateDto))) + .build(); + } + + @GetMapping("/{memberId}") + public ResponseEntity findMemberById(@PathVariable Long memberId) { + return ResponseEntity.ok(memberService.findMemberById(memberId)); + } + + @DeleteMapping("/{memberId}") + public ResponseEntity deleteMemberById(@PathVariable Long memberId){ + memberService.deleteMemberById(memberId); + return ResponseEntity.noContent().build(); + } + + @GetMapping + public ResponseEntity> getAllMembers() { + return ResponseEntity.ok(memberService.getAllMembers()); + } + +} diff --git a/demo/src/main/java/org/sopt/demo/controller/PostController.java b/demo/src/main/java/org/sopt/demo/controller/PostController.java new file mode 100644 index 0000000..24254c5 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/controller/PostController.java @@ -0,0 +1,27 @@ +package org.sopt.demo.controller; + +import lombok.RequiredArgsConstructor; +import org.sopt.demo.common.dto.SuccessStatusResponse; +import org.sopt.demo.service.PostService; +import org.sopt.demo.service.dto.PostCreateRequest; +import org.sopt.demo.common.dto.SuccessMessage; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1") +@RequiredArgsConstructor +public class PostController { + + private final PostService postService; + @PostMapping("/posts") + public ResponseEntity createPost( + @RequestHeader Long blogId, + @RequestBody PostCreateRequest postCreateRequest) { + return ResponseEntity.status(HttpStatus.CREATED).header( + "Location", + postService.create(blogId, postCreateRequest)) + .body(SuccessStatusResponse.of(SuccessMessage.POST_CREATE_SUCCESS)); + } +} diff --git a/demo/src/main/java/org/sopt/demo/domain/BaseTimeEntity.java b/demo/src/main/java/org/sopt/demo/domain/BaseTimeEntity.java new file mode 100644 index 0000000..b75cac4 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/domain/BaseTimeEntity.java @@ -0,0 +1,21 @@ +package org.sopt.demo.domain; + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + + +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseTimeEntity { + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; +} \ No newline at end of file diff --git a/demo/src/main/java/org/sopt/demo/domain/Blog.java b/demo/src/main/java/org/sopt/demo/domain/Blog.java new file mode 100644 index 0000000..50b6e25 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/domain/Blog.java @@ -0,0 +1,39 @@ +package org.sopt.demo.domain; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.sopt.demo.service.dto.BlogCreateRequest; +import org.sopt.demo.service.dto.BlogTitleUpdateRequest; + +@Entity +@Getter +@NoArgsConstructor +public class Blog extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + private Member member; + + @Column(length = 200) + private String title; + + private String description; + + private Blog(Member member, String title, String description) { + this.member = member; + this.title = title; + this.description = description; + } + + public static Blog create(Member member, BlogCreateRequest blogCreateRequest) { + return new Blog(member, blogCreateRequest.title(), blogCreateRequest.description()); + } + + public void updateTitle(BlogTitleUpdateRequest blogTitleUpdateRequest) { + this.title = blogTitleUpdateRequest.title(); + } +} diff --git a/demo/src/main/java/org/sopt/demo/domain/Member.java b/demo/src/main/java/org/sopt/demo/domain/Member.java new file mode 100644 index 0000000..d2e3533 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/domain/Member.java @@ -0,0 +1,43 @@ +package org.sopt.demo.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + + @Enumerated(EnumType.STRING) + private Part part; + + private int age; + + @Builder + private Member(String name, Part part, int age) { + this.name = name; + this.part = part; + this.age = age; + } + + public static Member create(String name, Part part, int age) { + return Member.builder() + .name(name) + .age(age) + .part(part) + .build(); + } +} diff --git a/demo/src/main/java/org/sopt/demo/domain/Part.java b/demo/src/main/java/org/sopt/demo/domain/Part.java new file mode 100644 index 0000000..b26a62d --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/domain/Part.java @@ -0,0 +1,5 @@ +package org.sopt.demo.domain; + +public enum Part { + SERVER, IOS, ANDROID, WEB, DESIGN, PLAN +} diff --git a/demo/src/main/java/org/sopt/demo/domain/Post.java b/demo/src/main/java/org/sopt/demo/domain/Post.java new file mode 100644 index 0000000..de0f1b3 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/domain/Post.java @@ -0,0 +1,41 @@ +package org.sopt.demo.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class Post extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + private Blog blog; + + public Post(String title, String content, Blog blog) { + this.title = title; + this.content = content; + this.blog = blog; + } + + public static Post create(Blog blog, String title, String content) { + Post post = new Post(); + post.blog = blog; + post.title = title; + post.content = content; + return post; + } +} diff --git a/demo/src/main/java/org/sopt/demo/exception/BusinessException.java b/demo/src/main/java/org/sopt/demo/exception/BusinessException.java new file mode 100644 index 0000000..bbfd3a4 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/exception/BusinessException.java @@ -0,0 +1,15 @@ +package org.sopt.demo.exception; + + +import lombok.Getter; +import org.sopt.demo.common.dto.ErrorMessage; + +@Getter +public class BusinessException extends RuntimeException{ + private ErrorMessage errorMessage; + + public BusinessException(ErrorMessage errorMessage) { + super(errorMessage.getMessage()); + this.errorMessage = errorMessage; + } +} diff --git a/demo/src/main/java/org/sopt/demo/exception/NotFoundException.java b/demo/src/main/java/org/sopt/demo/exception/NotFoundException.java new file mode 100644 index 0000000..7286a68 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/exception/NotFoundException.java @@ -0,0 +1,9 @@ +package org.sopt.demo.exception; + +import org.sopt.demo.common.dto.ErrorMessage; + +public class NotFoundException extends BusinessException { + public NotFoundException(ErrorMessage errorMessage) { + super(errorMessage); + } +} diff --git a/demo/src/main/java/org/sopt/demo/repository/BlogRepository.java b/demo/src/main/java/org/sopt/demo/repository/BlogRepository.java new file mode 100644 index 0000000..074bd9a --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/repository/BlogRepository.java @@ -0,0 +1,7 @@ +package org.sopt.demo.repository; + +import org.sopt.demo.domain.Blog; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BlogRepository extends JpaRepository { +} diff --git a/demo/src/main/java/org/sopt/demo/repository/MemberRepository.java b/demo/src/main/java/org/sopt/demo/repository/MemberRepository.java new file mode 100644 index 0000000..7555dc5 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/repository/MemberRepository.java @@ -0,0 +1,9 @@ +package org.sopt.demo.repository; + +import org.sopt.demo.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MemberRepository extends JpaRepository { +} diff --git a/demo/src/main/java/org/sopt/demo/repository/PostRepository.java b/demo/src/main/java/org/sopt/demo/repository/PostRepository.java new file mode 100644 index 0000000..a8dda22 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/repository/PostRepository.java @@ -0,0 +1,9 @@ +package org.sopt.demo.repository; + +import org.sopt.demo.domain.Post; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + + +} diff --git a/demo/src/main/java/org/sopt/demo/service/BlogService.java b/demo/src/main/java/org/sopt/demo/service/BlogService.java new file mode 100644 index 0000000..93aa379 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/BlogService.java @@ -0,0 +1,39 @@ +package org.sopt.demo.service; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.sopt.demo.common.dto.ErrorMessage; +import org.sopt.demo.domain.Blog; +import org.sopt.demo.domain.Member; +import org.sopt.demo.exception.NotFoundException; +import org.sopt.demo.repository.BlogRepository; +import org.sopt.demo.service.dto.BlogCreateRequest; +import org.sopt.demo.service.dto.BlogTitleUpdateRequest; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BlogService { + private final BlogRepository blogRepository; + private final MemberService memberService; + + + public String create(Long memberId, BlogCreateRequest blogCreateRequest) { + Member member = memberService.findById(memberId); + Blog blog = blogRepository.save(Blog.create(member, blogCreateRequest)); + return blog.getId().toString(); + } + + protected Blog findById(Long blogId) { + return blogRepository.findById(blogId).orElseThrow( + () -> new NotFoundException(ErrorMessage.BLOG_NOT_FOUND) + ); + } + + @Transactional + public void updateTitle(Long blogId, BlogTitleUpdateRequest blogTitleUpdateRequest) { + Blog blog = findById(blogId); + blog.updateTitle(blogTitleUpdateRequest); + } +} + diff --git a/demo/src/main/java/org/sopt/demo/service/MemberService.java b/demo/src/main/java/org/sopt/demo/service/MemberService.java new file mode 100644 index 0000000..61b0edb --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/MemberService.java @@ -0,0 +1,48 @@ +package org.sopt.demo.service; + +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.sopt.demo.domain.Member; +import org.sopt.demo.service.dto.MemberCreateDto; +import org.sopt.demo.service.dto.MemberFindDto; +import org.sopt.demo.repository.MemberRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class MemberService { + + private final MemberRepository memberRepository; + + @Transactional + public String createMember( + MemberCreateDto memberCreateDto + ) { + Member member = Member.create(memberCreateDto.name(), memberCreateDto.part(), memberCreateDto.age()); + memberRepository.save(member); + return member.getId().toString(); + } + + public MemberFindDto findMemberById(Long memberId) { + return MemberFindDto.of(memberRepository.findById(memberId).orElseThrow( + () -> new EntityNotFoundException("ID에 해당하는 사용자가 존재하지 않습니다.") + )); + } + + @Transactional + public void deleteMemberById(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("ID에 해당하는 사용자가 존재하지 않습니다.")); + memberRepository.delete(member); + } + + public List getAllMembers() { + return memberRepository.findAll().stream() + .map(MemberFindDto::of) + .collect(Collectors.toList()); + } +} diff --git a/demo/src/main/java/org/sopt/demo/service/PostService.java b/demo/src/main/java/org/sopt/demo/service/PostService.java new file mode 100644 index 0000000..03ce7c1 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/PostService.java @@ -0,0 +1,23 @@ +package org.sopt.demo.service; + + +import lombok.RequiredArgsConstructor; +import org.sopt.demo.domain.Blog; +import org.sopt.demo.domain.Post; +import org.sopt.demo.repository.PostRepository; +import org.sopt.demo.service.dto.PostCreateRequest; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class PostService { + + private final PostRepository postRepository; + private final BlogService blogService; + public String create(Long blogId, PostCreateRequest postCreateRequest) { + Blog blog = blogService.findById(blogId); + Post post = new Post(postCreateRequest.title(), postCreateRequest.content(),blog); + post = postRepository.save(post); + return post.getId().toString(); + } +} diff --git a/demo/src/main/java/org/sopt/demo/service/dto/BlogCreateRequest.java b/demo/src/main/java/org/sopt/demo/service/dto/BlogCreateRequest.java new file mode 100644 index 0000000..81d9923 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/dto/BlogCreateRequest.java @@ -0,0 +1,7 @@ +package org.sopt.demo.service.dto; + +public record BlogCreateRequest( + String title, + String description +) { +} diff --git a/demo/src/main/java/org/sopt/demo/service/dto/BlogTitleUpdateRequest.java b/demo/src/main/java/org/sopt/demo/service/dto/BlogTitleUpdateRequest.java new file mode 100644 index 0000000..d264699 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/dto/BlogTitleUpdateRequest.java @@ -0,0 +1,9 @@ +package org.sopt.demo.service.dto; + + +import jakarta.validation.constraints.Size; + +public record BlogTitleUpdateRequest( + @Size(max = 10, message = "블로그 제목이 최대 글자 수(10자)를 초과했습니다.") String title +) { +} \ No newline at end of file diff --git a/demo/src/main/java/org/sopt/demo/service/dto/MemberCreateDto.java b/demo/src/main/java/org/sopt/demo/service/dto/MemberCreateDto.java new file mode 100644 index 0000000..80f3b45 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/dto/MemberCreateDto.java @@ -0,0 +1,10 @@ +package org.sopt.demo.service.dto; + +import org.sopt.demo.domain.Part; + +public record MemberCreateDto( + String name, + Part part, + int age +) { +} \ No newline at end of file diff --git a/demo/src/main/java/org/sopt/demo/service/dto/MemberFindDto.java b/demo/src/main/java/org/sopt/demo/service/dto/MemberFindDto.java new file mode 100644 index 0000000..8401ddb --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/dto/MemberFindDto.java @@ -0,0 +1,15 @@ +package org.sopt.demo.service.dto; + +import org.sopt.demo.domain.Member; +import org.sopt.demo.domain.Part; + +public record MemberFindDto( + String name, + Part part, + int age +) { + public static MemberFindDto of(Member member) { + return new MemberFindDto(member.getName(), member.getPart(), member.getAge()); + } +} + diff --git a/demo/src/main/java/org/sopt/demo/service/dto/PostCreateRequest.java b/demo/src/main/java/org/sopt/demo/service/dto/PostCreateRequest.java new file mode 100644 index 0000000..d06a3c3 --- /dev/null +++ b/demo/src/main/java/org/sopt/demo/service/dto/PostCreateRequest.java @@ -0,0 +1,7 @@ +package org.sopt.demo.service.dto; + +public record PostCreateRequest( + String title, + String content + +) {} diff --git a/demo/src/main/resources/application.properties b/demo/src/main/resources/application.properties new file mode 100644 index 0000000..2109a44 --- /dev/null +++ b/demo/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=demo diff --git a/demo/src/test/java/org/sopt/demo/DemoApplicationTests.java b/demo/src/test/java/org/sopt/demo/DemoApplicationTests.java new file mode 100644 index 0000000..aae3196 --- /dev/null +++ b/demo/src/test/java/org/sopt/demo/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package org.sopt.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/demo/src/test/java/org/sopt/demo/controller/BlogControllerTest.java b/demo/src/test/java/org/sopt/demo/controller/BlogControllerTest.java new file mode 100644 index 0000000..27e0823 --- /dev/null +++ b/demo/src/test/java/org/sopt/demo/controller/BlogControllerTest.java @@ -0,0 +1,70 @@ +package org.sopt.demo.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.sopt.demo.repository.BlogRepository; +import org.sopt.demo.repository.MemberRepository; +import org.sopt.demo.service.BlogService; +import org.sopt.demo.service.MemberService; +import org.sopt.demo.service.dto.BlogCreateRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(BlogController.class) //SpringBootTest로 서버를 구동시키는 대신 Controller 계층만 테스트 +@AutoConfigureMockMvc //Spring Boot 테스트에서 MockMvc를 사용하기 위한 설정을 자동으로 제공하는 어노테이션 +public class BlogControllerTest { + + @Autowired + private MockMvc mockMvc; + + /* + BlogRepository -> BlogService -> MemberService -> MemberRepository + -> BlogRepository + */ + @SpyBean + private BlogService blogService; + + @SpyBean + private MemberService memberService; + + @MockBean + private MemberRepository memberRepository; + + @MockBean + private BlogRepository blogRepository; + + @Autowired + private ObjectMapper objectMapper; //생성하는 객체를 String JSON 배열로 바꾸기 위해 사용 + + @Nested + class createBlog { + @Test + @DisplayName("Blog 생성 실패 테스트") + public void createBlogFail() throws Exception { + + //given + String request = objectMapper.writeValueAsString(new BlogCreateRequest("소현이네 블로그", "블로그입니다.")); + + //when + mockMvc.perform( + post("/api/v1/blog") + .content(request).header("memberId", 2) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) //생성 실패 시나리오로 NotFound가 돌아오는 상황을 테스트 + .andDo(print()); // 끝난 후 모든 결과를 출력 + + } + } +} + diff --git a/demo/src/test/java/org/sopt/demo/controller/MemberControllerTest.java b/demo/src/test/java/org/sopt/demo/controller/MemberControllerTest.java new file mode 100644 index 0000000..1d56e3a --- /dev/null +++ b/demo/src/test/java/org/sopt/demo/controller/MemberControllerTest.java @@ -0,0 +1,53 @@ +package org.sopt.demo.controller; + + +import io.restassured.RestAssured; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.sopt.demo.domain.Part; +import org.sopt.demo.repository.MemberRepository; +import org.sopt.demo.service.MemberService; +import org.sopt.demo.service.dto.MemberCreateDto; +import org.sopt.demo.settings.ApiTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class MemberControllerTest extends ApiTest { + + @Autowired + private MemberService memberService; + + @Autowired + private MemberRepository memberRepository; + + @Nested + @DisplayName("멤버 생성 테스트") + public class CreateMember { + + @Test + @DisplayName("요청 성공 테스트") + public void createMemberSuccess() throws Exception { + //given + final var request = new MemberCreateDto( + "도소현", + Part.SERVER, + 24 + ); + + //when + final var response = RestAssured + .given() + .log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when() + .post("/api/v1/member") + .then().log().all().extract(); + //then + Assertions.assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); + } + } +} diff --git a/demo/src/test/java/org/sopt/demo/controller/TestController.java b/demo/src/test/java/org/sopt/demo/controller/TestController.java new file mode 100644 index 0000000..c25f07e --- /dev/null +++ b/demo/src/test/java/org/sopt/demo/controller/TestController.java @@ -0,0 +1,21 @@ +package org.sopt.demo.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test") +public class TestController { + @GetMapping + public String test(){ + return "test Api"; + } +// @GetMapping("/test/json") +// public ApiResponse testJson() { +// return ApiResponse.create("1차 세미나 테스트 API - JSON"); +// } +} + + + diff --git a/demo/src/test/java/org/sopt/demo/settings/Apitest.java b/demo/src/test/java/org/sopt/demo/settings/Apitest.java new file mode 100644 index 0000000..7068cde --- /dev/null +++ b/demo/src/test/java/org/sopt/demo/settings/Apitest.java @@ -0,0 +1 @@ +/**/ \ No newline at end of file