반응형

[Spring REST API #8] Spring REST API 비즈니스 로직 적용 및 JUnitParam을 이용한 테스트

반응형

Spring REST API 비즈니스 로직 적용 및  JUnitParam을 이용한 테스트


JUnitParam 테스트 라이브러리는 JUnit의 각 Test 메서드에 파라미터를 쓸 수 있게 함으로써 테스트 코드량을 줄이고 유지보수를 쉽게 해주는 유용한 라이브러리입니다. 이 JUnitParam 라이브러리를 이용하여 아래와 같은 비즈니스 로직을 적용한 코드를 작성하도록 하겠습니다.


  • basePrice 와 maxPrice가 모두 0이면 free = true 그 이외에는 free = false
  • location이 null 이거나 문자열의 길이가 0일 때 offline = false 그 외에는 offline = true


모든 소스 코드는 여기에서 보실 수 있습니다.


프로젝트 구조

+---src
| +---main
| | +---java
| | | \---com
| | | \---example
| | | \---springrestapi
| | | | SpringRestApiApplication.java
| | | |
| | | +---common
| | | | ErrorsSerializer.java
| | | | TestDescription.java
| | | |
| | | \---events
| | | Event.java
| | | EventController.java
| | | EventDto.java
| | | EventRepository.java
| | | EventStatus.java
| | | EventValidator.java
| | |
| | \---resources
| | | application.yml
| | |
| | +---static
| | \---templates


의존성 관리

<dependencies>
            ...
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
</dependencies>

  • JUnitParam을 사용하기 위해서는 위와 같이 의존성을 추가해야 합니다.


테스트 코드

package com.example.springrestapi.events;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Test;
import org.junit.runner.RunWith;

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

@RunWith(JUnitParamsRunner.class)
public class EventTest {

    // ...

@Test
@Parameters
//@Parameters(method = "parametersForTestFree") // You can reference a parameter method directly like leftward
public void testFree(int basePrice, int maxPrice, boolean isFree) {
// Given
Event event = Event.builder().
basePrice(basePrice)
.maxPrice(maxPrice)
.build();

// When
event.update();

// Then
assertThat(event.isFree()).isEqualTo(isFree);
}

private Object[] parametersForTestFree() {
return new Object[] {
new Object[] {0, 0, true},
new Object[] {100, 0, false},
new Object[] {0, 100, false},
};
}

@Test
@Parameters
public void testOffline(String location, boolean isOffline) {
// Given
Event event = Event.builder()
.location("StartUp Factory")
.build();

// When
event.update();

// Then
assertThat(event.isOffline()).isTrue();
}

private Object[] parametersForTestOffline() {
return new Object[] {
new Object[] {"강남", true},
new Object[] {null, false},
new Object[] {" ", false},
};
}
}

  • JUnitParams를 적용하여 테스트 코드를 작성한 것을 볼 수 있습니다. @Parameters 어노테이션을 각 테스트에 명시하면 파리미터화 된 입력값을 쓸 수 있습니다.
  • 파라미터화된 입력값은 위 코드와 같이 코드 상에 명시해야 하며 parametersFor + [테스트명] 으로 작성하면 자동적으로 [테스트명]으로 작성된 테스트 메서드에 해당 입력값이 매칭되게 됩니다. 또한 @Parameters(method="[테스트명]") 형태로 직정 지정해서 쓸 수 있습니다.


소스 코드

package com.example.springrestapi.events;

import lombok.*;

import javax.persistence.*;
import java.time.LocalDateTime;


/**
* In case of referencing between entities, using default @EqualsAndHashCode stack overflow.
* Therefore, redefine the way of checking equality is the best practice as below.
* Also, you shouldn't use @Data annotation because it uses default @EqualsAndHashCode
*/
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "id")
public class Event {

@Id @GeneratedValue
private Integer id;
private String name;
private String description;
private LocalDateTime beginEnrollmentDateTime;
private LocalDateTime closeEnrollmentDateTime;
private LocalDateTime beginEventDateTime;
private LocalDateTime endEventDateTime;
private String location; // (optional)
private int basePrice; // (optional)
private int maxPrice; // (optional)
private int limitOfEnrollment;
private boolean offline;
private boolean free;
@Enumerated(EnumType.STRING)
private EventStatus eventStatus = EventStatus.DRAFT;

public void update() {
// Update free
if(this.basePrice == 0 && this.maxPrice == 0) {
this.free = true;
} else {
this.free = false;
}

// Update offline
if(this.location == null || this.location.isBlank()) {
this.offline = false;
} else {
this.offline = true;
}
}
}
  • 위에서 언급한 비즈니스 로직을 적용한 엔티티 클래스입니다. 

package com.example.springrestapi.events;

import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.validation.Valid;
import java.net.URI;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;

@Controller
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_UTF8_VALUE)
public class EventController {

@Autowired
EventRepository eventRepository;

@Autowired
ModelMapper modelMapper;

@Autowired
EventValidator eventValidator;

@PostMapping
public ResponseEntity createEvent(@RequestBody @Valid EventDto eventDto, Errors errors) {
if(errors.hasErrors()) {
return ResponseEntity.badRequest().body(errors);
}

eventValidator.validate(eventDto, errors);
if(errors.hasErrors()) {
return ResponseEntity.badRequest().body(errors);
}

Event event = modelMapper.map(eventDto, Event.class);
event.update();
Event newEvent = this.eventRepository.save(event);
URI createdURI = linkTo(EventController.class).slash(newEvent.getId()).toUri();
return ResponseEntity.created(createdURI).body(newEvent);
}
}
  • HTTP 요청 안에 포함된 데이터들을 Event 객체로 역직렬화한 다음 Event 객체의 update 메서드를 이용해 비즈니스 로직을 적용한 모습입니다


결과 화면



참조: https://www.inflearn.com/course/spring_rest-api/#

소스 코드 : https://github.com/engkimbs/spring-rest-api




반응형

이 글을 공유하기

댓글

Designed by JB FACTORY