Skip to content

Commit

Permalink
test: 차량 관리 단위 테스트 설계 (#46)
Browse files Browse the repository at this point in the history
* test: 차량 엔티티 테스트

* test: 차량 재고 엔티티 테스트

* test: 차량 예약 엔티티 테스트

* test: 차량 시험 수행 엔티티 테스트

* refactor: 엔티티 팩토리 구현 및 리팩토링

* test: 시험 수행 이력 서비스 단위 테스트 구현

* test: 차량 재고 서비스 단위 테스트 구현

* test: 시험차량 예약 테스트 및 리팩토링

* test: 차량 재고 테스트 수정

* feat: 차량 서비스 단위 테스트 구현
  • Loading branch information
gengminy authored Dec 12, 2023
1 parent 827affb commit 358475c
Show file tree
Hide file tree
Showing 31 changed files with 1,490 additions and 25 deletions.
13 changes: 0 additions & 13 deletions src/main/java/com/testcar/car/domains/car/CarService.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,11 @@ public Car findById(Long id) {
.orElseThrow(() -> new NotFoundException(ErrorCode.CAR_NOT_FOUND));
}

/** 차량을 이름으로 조회합니다. */
public Car findByName(String name) {
return carRepository
.findByNameAndDeletedFalse(name)
.orElseThrow(() -> new NotFoundException(ErrorCode.CAR_NOT_FOUND));
}

/** 차량을 조건에 맞게 조회합니다. */
public Page<Car> findAllPageByCondition(CarFilterCondition condition, Pageable pageable) {
return carRepository.findAllPageByCondition(condition, pageable);
}

/** 시험 차량을 조건에 맞게 조회합니다. */
public Page<Car> findAllWithStocksPageByCondition(
CarFilterCondition condition, Pageable pageable) {
return carRepository.findAllWithStocksPageByCondition(condition, pageable);
}

/** 새로운 차량을 등록합니다. */
public Car register(RegisterCarRequest request) {
final Car car = createEntity(request);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/testcar/car/domains/car/entity/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
@Getter
@RequiredArgsConstructor
public enum Type {
HATCHBACK("해치백"),
SEDAN("세단"),
WAGON("왜건"),
SUV("SUV"),
TRUCK("트럭"),
VAN("승합차");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Builder;
import lombok.Getter;
import org.hibernate.validator.constraints.Length;

@Getter
@Builder
public class RegisterCarRequest {
@NotBlank(message = "차량명을 입력해주세요.")
@Length(max = 20)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.testcar.car.common.exception.BadRequestException;
import com.testcar.car.common.exception.NotFoundException;
import com.testcar.car.domains.carReservation.entity.CarReservation;
import com.testcar.car.domains.carReservation.entity.ReservationStatus;
import com.testcar.car.domains.carReservation.exception.ErrorCode;
Expand Down Expand Up @@ -44,7 +45,7 @@ public List<CarReservation> findAllByMemberAndIds(Member member, List<Long> ids)
final List<CarReservation> carReservations =
carReservationRepository.findAllWithCarStockByIdInAndMemberId(ids, member.getId());
if (carReservations.size() != ids.size()) {
throw new BadRequestException(ErrorCode.CAR_RESERVATION_NOT_FOUND);
throw new NotFoundException(ErrorCode.CAR_RESERVATION_NOT_FOUND);
}
return carReservations;
}
Expand All @@ -63,7 +64,6 @@ public CarReservation reserve(Member member, CarReservationRequest request) {
.startedAt(now)
.expiredAt(expiredAt)
.build();
carStock.updateStatus(StockStatus.RESERVED);
carStockRepository.save(carStock);
return carReservationRepository.save(carReservation);
}
Expand All @@ -73,17 +73,24 @@ public List<CarReservation> returnCarReservation(
Member member, ReturnCarReservationRequest request) {
final List<CarReservation> carReservations =
this.findAllByMemberAndIds(member, request.getCarReservationIds());
carReservations.forEach(CarReservation::updateReturn);

carReservations.forEach(
carReservation -> {
validateCarReservationReserved(carReservation);
carReservation.updateReturn();
});
final List<CarStock> carStocks =
carReservations.stream().map(CarReservation::getCarStock).toList();
carStocks.forEach(carStock -> carStock.updateStatus(StockStatus.AVAILABLE));

carStockRepository.saveAll(carStocks);
carStockService.returnCarStocks(carStocks);
return carReservationRepository.saveAll(carReservations);
}

public void validateCarStockAvailable(CarStock carStock) {
private void validateCarReservationReserved(CarReservation carReservation) {
if (carReservation.getStatus() != ReservationStatus.RESERVED) {
throw new BadRequestException(ErrorCode.CAR_RESERVATION_NOT_RESERVED);
}
}

private void validateCarStockAvailable(CarStock carStock) {
if (carStock.getStatus() != StockStatus.AVAILABLE) {
throw new BadRequestException(ErrorCode.CAR_STOCK_NOT_AVAILABLE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
@RequiredArgsConstructor
public enum ErrorCode implements BaseErrorCode {
CAR_STOCK_NOT_AVAILABLE("CRS001", "해당 재고는 대여 불가합니다."),
CAR_RESERVATION_NOT_FOUND("CRS002", "해당 예약 정보를 찾을 수 없습니다.");
CAR_RESERVATION_NOT_FOUND("CRS002", "해당 예약 정보를 찾을 수 없습니다."),
CAR_RESERVATION_NOT_RESERVED("CRS003", "예약 중인 차량만 반납 가능합니다.");

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CarReservationRequest {
@NotNull(message = "차량재고 ID를 입력해주세요.")
@Positive(message = "차량재고 ID는 0보다 커야합니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class ReturnCarReservationRequest {
@NotNull(message = "차량 예약 ID 리스트를 입력해주세요.")
@NotEmpty(message = "차량 예약 ID 리스트를 입력해주세요.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.testcar.car.domains.car.CarService;
import com.testcar.car.domains.car.entity.Car;
import com.testcar.car.domains.carStock.entity.CarStock;
import com.testcar.car.domains.carStock.entity.StockStatus;
import com.testcar.car.domains.carStock.exception.ErrorCode;
import com.testcar.car.domains.carStock.model.DeleteCarStockRequest;
import com.testcar.car.domains.carStock.model.RegisterCarStockRequest;
Expand Down Expand Up @@ -73,11 +74,22 @@ public CarStock updateById(Long carStockId, UpdateCarStockRequest request) {
return carStockRepository.save(carStock);
}

/** 예약 중인 재고를 모두 반납합니다. */
public List<CarStock> returnCarStocks(List<CarStock> carStocks) {
carStocks.forEach(
carStock -> {
validateCarStockReserved(carStock);
carStock.updateStatus(StockStatus.AVAILABLE);
});
return carStockRepository.saveAll(carStocks);
}

/** 재고를 삭제 처리 합니다. (soft delete) */
public List<CarStock> deleteAll(DeleteCarStockRequest request) {
public List<Long> deleteAll(DeleteCarStockRequest request) {
final List<CarStock> stocks = this.findAllByIdIn(request.getIds());
stocks.forEach(CarStock::delete);
return carStockRepository.saveAll(stocks);
carStockRepository.saveAll(stocks);
return request.getIds();
}

/** 영속되지 않은 재고 엔티티를 생성합니다. */
Expand All @@ -96,4 +108,11 @@ private void validateStockNumberNotDuplicated(String stockNumber) {
throw new BadRequestException(ErrorCode.DUPLICATED_STOCK_NUMBER);
}
}

/** 차량 재고가 예약 상태인지 검사합니다. */
private void validateCarStockReserved(CarStock carStock) {
if (carStock.getStatus() != StockStatus.RESERVED) {
throw new BadRequestException(ErrorCode.CAR_STOCK_NOT_RESERVED);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
@RequiredArgsConstructor
public enum ErrorCode implements BaseErrorCode {
CAR_STOCK_NOT_FOUND("STK001", "해당 재고를 찾을 수 없습니다."),
DUPLICATED_STOCK_NUMBER("STK002", "중복된 재고번호입니다.");
DUPLICATED_STOCK_NUMBER("STK002", "중복된 재고번호입니다."),
CAR_STOCK_NOT_RESERVED("STK003", "예약중인 차량이 아닙니다.");

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import java.util.List;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class DeleteCarStockRequest {
@NotNull(message = "차량 재고 ID를 입력해주세요.")
@NotEmpty(message = "차량 재고 ID를 입력해주세요.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class RegisterCarStockRequest {
@NotNull(message = "차량 ID를 입력해주세요.")
@Positive(message = "차량 ID는 0보다 커야합니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class UpdateCarStockRequest {
@NotBlank(message = "차량 재고번호를 입력해주세요.")
@Pattern(regexp = "^[0-9]{12}$", message = "차량 재고번호는 12자리 숫자만 가능합니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CarTestRequest {
@NotBlank(message = "시험장 이름을 입력해주세요.")
@Schema(description = "시험장 이름", example = "서산주행시험장")
Expand Down
68 changes: 68 additions & 0 deletions src/test/java/com/testcar/car/common/CarEntityFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.testcar.car.common;

import static com.testcar.car.common.Constant.CAR_DISPLACEMENT;
import static com.testcar.car.common.Constant.CAR_NAME;
import static com.testcar.car.common.Constant.CAR_STOCK_NUMBER;
import static com.testcar.car.common.Constant.CAR_TEST_RESULT;
import static com.testcar.car.common.Constant.CAR_TYPE;
import static com.testcar.car.common.Constant.EXPIRED_AT;
import static com.testcar.car.common.Constant.STARTED_AT;

import com.testcar.car.domains.car.entity.Car;
import com.testcar.car.domains.car.entity.Car.CarBuilder;
import com.testcar.car.domains.carReservation.entity.CarReservation;
import com.testcar.car.domains.carReservation.entity.CarReservation.CarReservationBuilder;
import com.testcar.car.domains.carReservation.entity.ReservationStatus;
import com.testcar.car.domains.carStock.entity.CarStock;
import com.testcar.car.domains.carStock.entity.CarStock.CarStockBuilder;
import com.testcar.car.domains.carStock.entity.StockStatus;
import com.testcar.car.domains.carTest.entity.CarTest;
import com.testcar.car.domains.carTest.entity.CarTest.CarTestBuilder;

public class CarEntityFactory {
private CarEntityFactory() {}

public static Car createCar() {
return createCarBuilder().build();
}

public static CarBuilder createCarBuilder() {
return Car.builder().name(CAR_NAME).displacement(CAR_DISPLACEMENT).type(CAR_TYPE);
}

public static CarStock createCarStock() {
return createCarStockBuilder().build();
}

public static CarStockBuilder createCarStockBuilder() {
return CarStock.builder()
.car(createCar())
.stockNumber(CAR_STOCK_NUMBER)
.status(StockStatus.AVAILABLE);
}

public static CarReservation createCarReservation() {
return createCarReservationBuilder().build();
}

public static CarReservationBuilder createCarReservationBuilder() {
return CarReservation.builder()
.member(MemberEntityFactory.createMember())
.carStock(createCarStock())
.startedAt(STARTED_AT)
.expiredAt(EXPIRED_AT)
.status(ReservationStatus.RESERVED);
}

public static CarTest createCarTest() {
return createCarTestBuilder().build();
}

public static CarTestBuilder createCarTestBuilder() {
return CarTest.builder()
.member(MemberEntityFactory.createMember())
.carStock(createCarStock())
.performedAt(STARTED_AT)
.result(CAR_TEST_RESULT);
}
}
38 changes: 38 additions & 0 deletions src/test/java/com/testcar/car/common/Constant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.testcar.car.common;


import com.testcar.car.domains.car.entity.Type;
import com.testcar.car.domains.member.Role;
import java.time.LocalDateTime;

public class Constant {
private Constant() {}

/** Member */
public static final String MEMBER_EMAIL = "[email protected]";

public static final String MEMBER_PASSWORD = "1234abcd@";
public static final String MEMBER_NAME = "홍길동";
public static final Role MEMBER_ROLE = Role.ADMIN;
public static final String DEPARTMENT_NAME = "모비스시스템팀";

/** Car */
public static final String CAR_NAME = "아반떼";

public static final String ANOTHER_CAR_NAME = "소나타";
public static final double CAR_DISPLACEMENT = 1.6;
public static final Type CAR_TYPE = Type.SEDAN;
public static final String CAR_STOCK_NUMBER = "123456789012";
public static final String ANOTHER_CAR_STOCK_NUMBER = "987654321098";
public static final LocalDateTime STARTED_AT = LocalDateTime.of(2021, 1, 1, 0, 0, 0);
public static final LocalDateTime EXPIRED_AT = LocalDateTime.of(2021, 1, 8, 0, 0, 0);
public static final String CAR_TEST_RESULT = "통과";

/** Track */
public static final String TRACK_NAME = "서산주행시험장";

public static final String ANOTHER_TRACK_NAME = "마포주행시험장";
public static final String TRACK_LOCATION = "충청남도 서산시 부석면";
public static final String TRACK_DESCRIPTION = "비탈길";
public static final double TRACK_LENGTH = 12.6;
}
30 changes: 30 additions & 0 deletions src/test/java/com/testcar/car/common/DtoFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.testcar.car.common;

import static com.testcar.car.common.CarEntityFactory.createCarStock;
import static com.testcar.car.common.CarEntityFactory.createCarTest;
import static com.testcar.car.common.TrackEntityFactory.createTrack;

import com.testcar.car.domains.car.entity.Car;
import com.testcar.car.domains.carStock.entity.CarStock;
import com.testcar.car.domains.carTest.model.vo.CarTestDto;
import com.testcar.car.domains.department.entity.Department;
import com.testcar.car.domains.member.Member;

public class DtoFactory {
private DtoFactory() {}

public static CarTestDto createCarTestDto() {
final Member member = MemberEntityFactory.createMember();
final Department department = member.getDepartment();
final CarStock carStock = createCarStock();
final Car car = carStock.getCar();
return new CarTestDto(
createCarTest(),
createTrack(),
member.getName(),
member.getName(),
department.getName(),
car.getName(),
carStock.getStockNumber());
}
}
Loading

0 comments on commit 358475c

Please sign in to comment.