본문 바로가기
Spring

SpringBoot 간단한 CRUD REST API 구현 및 JUnit5로 테스트하기

by wadekang 2022. 2. 26.

REST API란?

REST API는 REST(representational state transfer)를 기반으로 하여 HTTP 요청을 사용하여 데이터에 액세스하고 사용하는 API의 아키텍처 스타일입니다.

 

CRUD란?

API를 구축할 때 모델이 제공하는 4가지 기본 유형의 기능, Create(생성), Read(읽기), Update(수정), Delete(삭제)를 CRUD라고 합니다.

 

CRUD 구현 (Entity, Repository, Service, Controller)

JPA와 H2 데이터베이스를 사용하여 간단하게 구현했습니다.

User.java (Entity)
package com.example.practice.impl;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Getter
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private int age;

    private String address;

    @Builder
    public User(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public void update(String address) {
        this.address = address;
    }
}
UserRepository.java (Repository)
package com.example.practice.impl;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}
UserService.java (Service)
package com.example.practice.impl;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;

    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다."));
    }

    @Transactional
    public Long save(User user) {
        return userRepository.save(user).getId();
    }

    @Transactional
    public Long update(Long id, String address) {
        User user = findById(id);
        user.update(address);
        return id;
    }

    @Transactional
    public void delete(Long id) {
        User user = findById(id);
        userRepository.delete(user);
    }
}
UserController.java (Controller)
package com.example.practice.impl;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
public class UserController {

    private final UserService userService;

    @PostMapping("/user")
    public Long create(@RequestBody User user) {
        return userService.save(user);
    }

    @GetMapping("/user/{id}")
    public User read(@PathVariable Long id) {
        return userService.findById(id);
    }

    @PostMapping("/user/{id}/update")
    public Long update(@PathVariable Long id, @RequestBody String address) {
        return userService.update(id, address);
    }

    @PostMapping("/user/{id}/delete")
    public Long delete(@PathVariable Long id) {
        userService.delete(id);
        return id;
    }
}

 

JUnit5로 CRUD 테스트

  • Create Test
@Test
void create_test() {
    // given
    String name = "John Doe";
    int age = 27;
    String address = "Seoul";

    User user = User.builder()
            .name(name)
            .age(age)
            .address(address)
            .build();

    String url = "http://localhost:" + port + "/user";

    // when
    ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, user, Long.class);

    // then
    assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

    User savedUser = userService.findById(responseEntity.getBody());
    assertThat(savedUser.getName()).isEqualTo(name);
    assertThat(savedUser.getAge()).isEqualTo(age);
    assertThat(savedUser.getAddress()).isEqualTo(address);
}

TestRestTemplate을 이용해 POST 요청으로 유저의 정보를 넘겨 DB에 저장하고, DB에서 해당 유저의 정보를 조회하여 입력한 정보와 같은지 검증

 

  • Read Test
@Test
void read_test() {
    // given
    String name = "John Doe";
    int age = 27;
    String address = "Seoul";

    Long savedId = userRepository.save(User.builder()
            .name(name)
            .age(age)
            .address(address)
            .build()).getId();

    String url = "http://localhost:" + port + "/user/" + savedId;

    // when
    ResponseEntity<User> responseEntity = restTemplate.getForEntity(url, User.class);

    // then
    assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

    User readUser = responseEntity.getBody();
    assertThat(readUser.getName()).isEqualTo(name);
    assertThat(readUser.getAge()).isEqualTo(age);
    assertThat(readUser.getAddress()).isEqualTo(address);
}

DB에 유저 정보를 하나 저장한 후 GET 요청으로 해당 유저를 조회하여 정보가 같은지 비교합니다. 

 

  • Update Test
@Test
void update_test() {
    // given
    String name = "John Doe";
    int age = 27;
    String address = "Seoul";

    Long savedId = userRepository.save(User.builder()
            .name(name)
            .age(age)
            .address(address)
            .build()).getId();

    String url = "http://localhost:" + port + "/user/" + savedId + "/update";
    String address2 = "Busan";

    // when
    ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, address2, Long.class);

    // then
    assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

    User updatedUser = userService.findById(savedId);
    assertThat(updatedUser.getAddress()).isEqualTo(address2);
}

DB에 유저 정보를 하나 저장한 후 /user/id/update의 경로로 POST 요청, 요청할 때 수정할 정보인 address2를 전달합니다. 요청이 종료된 후 DB에서 유저 정보를 조회하여 update 요청 시 전달한 정보와 같은지 조회합니다.  

 

  • Delete Test
@Test
void delete_test() {
    // given
    String name = "John Doe";
    int age = 27;
    String address = "Seoul";

    Long savedId = userRepository.save(User.builder()
            .name(name)
            .age(age)
            .address(address)
            .build()).getId();

    String url = "http://localhost:" + port + "/user/" + savedId + "/delete";

    // when
    ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, null, Long.class);

    // then
    assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

    assertThatThrownBy(() -> userService.findById(savedId)).isInstanceOf(IllegalArgumentException.class);
}

DB에 유저 정보를 하나 저장한 후, /user/id/delete의 경로로 POST 요청을 통해 해당 유저 정보를 삭제합니다. 요청이 완료된 후 DB에서 해당 유저 정보를 조회하여 IllegalArgumentException이 발생하는지 확인합니다. UserService.java에서 유저 정보를 조회할 때 해당 유저가 존재하지 않는다면 해당 예외가 발생하도록 구현했습니다.

 

 전체 테스트를 실행하면 잘 통과되는 것을 확인하실 수 있습니다.

 

UserControllerTest.java
package com.example.practice.web;

import com.example.practice.impl.User;
import com.example.practice.impl.UserRepository;
import com.example.practice.impl.UserService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.Assertions.*;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {

    @LocalServerPort
    int port;

    @Autowired
    TestRestTemplate restTemplate;

    @Autowired
    UserRepository userRepository;

    @Autowired
    UserService userService;

    @AfterEach
    public void afterEach() {
        userRepository.deleteAll();
    }

    @Test
    void create_test() {
        // given
        String name = "John Doe";
        int age = 27;
        String address = "Seoul";

        User user = User.builder()
                .name(name)
                .age(age)
                .address(address)
                .build();

        String url = "http://localhost:" + port + "/user";

        // when
        ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, user, Long.class);

        // then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

        User savedUser = userService.findById(responseEntity.getBody());
        assertThat(savedUser.getName()).isEqualTo(name);
        assertThat(savedUser.getAge()).isEqualTo(age);
        assertThat(savedUser.getAddress()).isEqualTo(address);
    }

    @Test
    void read_test() {
        // given
        String name = "John Doe";
        int age = 27;
        String address = "Seoul";

        Long savedId = userRepository.save(User.builder()
                .name(name)
                .age(age)
                .address(address)
                .build()).getId();

        String url = "http://localhost:" + port + "/user/" + savedId;

        // when
        ResponseEntity<User> responseEntity = restTemplate.getForEntity(url, User.class);

        // then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

        User readUser = responseEntity.getBody();
        assertThat(readUser.getName()).isEqualTo(name);
        assertThat(readUser.getAge()).isEqualTo(age);
        assertThat(readUser.getAddress()).isEqualTo(address);
    }

    @Test
    void update_test() {
        // given
        String name = "John Doe";
        int age = 27;
        String address = "Seoul";

        Long savedId = userRepository.save(User.builder()
                .name(name)
                .age(age)
                .address(address)
                .build()).getId();

        String url = "http://localhost:" + port + "/user/" + savedId + "/update";
        String address2 = "Busan";

        // when
        ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, address2, Long.class);

        // then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

        User updatedUser = userService.findById(savedId);
        assertThat(updatedUser.getAddress()).isEqualTo(address2);
    }

    @Test
    void delete_test() {
        // given
        String name = "John Doe";
        int age = 27;
        String address = "Seoul";

        Long savedId = userRepository.save(User.builder()
                .name(name)
                .age(age)
                .address(address)
                .build()).getId();

        String url = "http://localhost:" + port + "/user/" + savedId + "/delete";

        // when
        ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, null, Long.class);

        // then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

        assertThatThrownBy(() -> userService.findById(savedId)).isInstanceOf(IllegalArgumentException.class);
    }
}

댓글