테스트 코드

이선우·2024년 7월 20일
0

개발 관련 지식

목록 보기
2/13
post-custom-banner

기간 과제로 테스트 코드에 대한 공부가 주어져 이 블로그에 정리를 해보기로 했다.

과거에 테스트는 모두 사람이 하는 것이었고, 한 번의 테스트는 상당한 노동력을 필요로 했다.

하지만 로직이 대부분 쿼리에 있는 mybatis에서는 테스트하기가 상당히 까다로워 시간이 지나면서 JPA를 하게되고쿼리가 아닌 자바 코드에 로직이 많이 담기게 되었다.

유지보수의 극적인 향상이 일어났다.

테스트를 잘 하기 위한 기반

  • 클래스나 메서드가 SRP를 잘 지키고, 크기가 작아야 한다(한 메서드에 너무 많은 테스트를 수행하지 않아도 되며 이로 인해 자연스럽게 역할이 확인되면서 쪼개진다)
  • 적절한 Mocking을 통한 격리성 확보(단위테스트가 만능은 아니지만, 위의 SRP처럼 해당 메서드의 역할을 정확히 테스트하려면 주변 조건을 적절히 통제 해야한다)
  • 당연히 잘 돌겠지라는 생각말고 꼼꼼히 테스트 && 너무 과도하게 많은 테스트와 코드량 X
  • 테스트코드도 리팩토링이 필요하며, 기법에 대한 지속적인 고민이 필요하다

Junit5

Java 프로그래밍 언어를 위한 다양한 유닛 테스트 프레임워크.

JUnit 5의 주요 기능
1. 테스트 생명주기 확장(Extension Model)
2. 다양한 테스트 용어
@Test, @BeforeEach, @AfterEach와 같은 애노테이션 대신에 JUnit 5에서는 위의 세 가지 이외에도 @BeforeAll, @AfterAll, @DisplayName, @Nested등 다양한 애노테이션을 제공
3. 조건 기반 실행(Conditional Test Execution)
@EnableOnOs, @EnabledIf, @DisabledIf 등의 애노테이션을 사용해서 조건에 따라 테스트를 실행하거나 스킵할 수 있다.
4. 동적 테스트(Dynamic Tests)
@TestFactory를 사용해서 런타임에 동적으로 테스트를 생성할 수 있다. 이는 반복적인 테스트 케이스 생성이나 매개변수화된 테스트를 구현하는 데 유용하다
5. 파라미터화 테스트
여러 다른 입력값을 사용해 반복적으로 테스트 가능
6. 확장 모델
테스트 실행을 확장하고 커스터마이징 가능
7. 자동 리소스 관리
테스트 실행중에 임시 디렉토리나 파일을 자동으로 생성 및 정리 가능


spring boot start에 Junit5가 기본적으로 포함되어있다.

Junit이란 자바의 유닛테스트를 위한 라이브러리!!
Junit안에는 @SpringBootTest가 있다.

@SpringBootTest: 어플리케이션을 띄우는 것과 마찬가지로 테스트 할 때에도 모든 빈을 띄워서 테스트하겠다!

@SpringBootTest를 사용하면 빈을 사용할 때에 @Autowired만 써주면 된다.

Mockito

Java를 위한 인기 있는 모킹(Mock) 프레임워크로, 객체 지향 프로그래밍에서 단위 테스트를 작성할 때 사용된다. 외부 의존성을 모킹하여 테스트를 더 효과적으로 수행할 수 있도록 도와준다.

모킹은 테스트 중에 실제 객체 대신에 가짜 객체를 사용해서 테스트 대상을 고립시키고 외부 요인의 영향을 배제할 수 있게 해준다.

@ExtendWith(MockitoExtension.class)
외부 기능인 MockitoExtension을 사용하겠다.

@Mock
private DeveloperRepository developerRepository;

@InjectMocks
private DMakerService dMakerService;

@Mock이 developerRepository를 @InjectMocks가 붙은 dMakerService가 생성될 때 자동으로 생성해준다

@WebMVCTest(): 컨트롤러와 관련된 빈들만 띄어준다. @Mock과 유사하다


그럼 또 한번 위에서 말한 내용을 정리해서 써보도록 하겠다.

Junit 테스트에는 대표적으로 @WebMvcTest와 @SpringBootTest를 대표적으로 사용하는데 두 가지의 어노테이션에는 차이가 존재한다.

Mock이란?

Mock은 위에서 말했듯이 실제 객체를 만들어서 테스트하기가 어려운 경우에, 가짜 객체를 만들어서 테스트하는 기술이다.

MockMVC란?

MVC에 관련된 Mock 가짜 객체를 말한다.
웹 어플리케이션을 서버에 배포하지 않고, 테스트용 MVC 환경을 만들어서 요청 및 전송, 응답 기능을 제공해주는 객체이다.
대부분의 어플리케이션 기능을 테스트하기 위해서는 MockMVC 객체를 만들어서 테스트하게 되는데, MockMVC를 @Autowired로 주입받아서 사용할 수 있다.

이런식으로 MockMvc를 @Autowired로 주입 받을 때 두 어노테이션의 차이가 존재하며 빈의 등록범위에도 차이가 존재한다.

Mock 주입 시 차이

@SpringBootTest
class SpringBootTest {

    @Autowired
    MockMvc mockMvc; // 주입 X
}

@SpringBootTest만 선언하고 MockMvc를 @Autowired로 주입받으려고 하면, 주입이 되지 않아 오류가 발생한다. @SpringBootTest는 MockMvc를 빈으로 등록시키지 않기 때문에 @AutoConfigureMockMvc 어노테이션을 사용해야 한다.

@SpringBootTest
@AutoConfigureMockMvc
class SpringBootTest {

    @Autowired
    MockMvc mockMvc; // 주입 O
}
@WebMvcTest
class SpringBootTest {

    @Autowired
    MockMvc mockMvc; // 주입 O
}

@WebMvcTest는 MockMvc를 빈으로 등록하기 때문에 다른 어노테이션이 필요 없다.

빈의 등록 범위 차이

@SpringBootTest
class SpringBootTest {

    @Autowired
    MockMvc mockMvc; // 주입 O

    @Autowired
    UserController userController; // 주입 O

    @Autowired
    UserRepository userRepository; // 주입 O

    @Autowired
    UserService userService; // 주입 O
}

@SpringBootTest에서는 프로젝트의 컨트롤러, 리포지토리, 서비스가 @Autowired로 다 주입된다.

@WebMvcTest
class SpringBootTest {

		@Autowired
		MockMvc mockMvc; // 주입 O

		@Autowired
		UserController userController; // 주입 O

		@Autowired
		UserRepository userRepository; // 주입 X

		@Autowired
		UserService userService; // 주입 X
}

@WebMvcTest에서는 Web Layer관련 빈들만 등록하기 때문에 컨트롤러는 주입이 정상적으로 되지만, @Component로 등록된 리포지토리와 서비스는 주입이 되지 않는다. 따라서 @WebMvcTest에서 리포지토리와 서비스를 사용하기 위해서는 @MockBean을 사용해 리포지토리와 서비스를 Mock객체에 빈으로 등록해줘야 한다.

@WebMvcTest
class SpringBootTest {

    @Autowired
    MockMvc mockMvc; // 주입 O

    @Autowired
    UserController userController; // 주입 O

    @MockBean
    UserRepository userRepository; // 주입 O

    @MockBean
    UserService userService; // 주입 O
}

@SpringBootTest와 @WebMvcTest 차이 결론

@SpringBootTest

  • MockMvc 객체를 빈으로 등록하지 않기 때문에 @AutoConfigureMockMvc로 빈으로 등록해야한다.
  • 프로젝트에 있는 스프링 빈을 모두 등록해서 테스트에 필요한 의존성을 추가해준다.

장점

  1. 프로젝트에 있는 모든 스프링 빈을 등록하므로, 테스트에 필요한 객체를 주입받아서 쉽게 사용 가능하다
  2. 실제 환경과 가장 유사하게 테스트가 가능하다.

단점

  1. 모든 스프링 빈을 등록할 때 프로젝트의 전체 컨텍스트를 로드해서 빈을 주입하기 때문에 속도가 느리다.
  2. 테스트 단위가 크기 때문에 디버깅이 어려울 수 있다.

위와 같은 이유로, 단위 테스트와 같은 기능 테스트가 아닌 전체적인 프로그램 작동이 제대로 이루어 지는지 검증하는 통합 테스트 시에 많이 사용한다!!!

@WebMvcTest

  • MockMvc 객체를 빈으로 등록해서 @Autowired로 MockMvc 주입이 가능하다
  • Web Layer관련 빈들만 등록하기 때문에, @Component로 등록한 빈은 @MockBean으로 등록해야 한다.

장점

  1. Web Layer 관련 빈만 로드하기 때문에, 속도가 @SpringBootTest보다 빠르다.
  2. 통합테스트에서 테스트가 어려운 작은 단위 테스트들을 @WebMvcTest로 진행 할 수 있다.

단점

  1. Mock 객체를 사용하기 때문에 실제 환경에서는 다른 오류가 발생할 수 있다.

위와 같은 이유로 컨트롤러 테스트, 단위 테스트 시에 많이 이용한다



테스트 코드를 작성하는 이유는
1. 문서화 역할
2. 코드에 결함을 발견하기 위함
3. 리팩토링 시 안정성 확보
4. 테스트 하기 쉬운 코드를 작성하다 보면 더 낮은 결합도를 설계를 얻을 수 있음

TDD

  • Test Driven Development(테스트 주도 개발)
  • 프로덕션 코드보다 테스트 코드를 먼저 작성하는 개발 방법
  • TFD(Test First Development) + 리팩토링
  • 기능 동작을 검증(메소드 단위)

BDD

  • Behavior Driven Deveopmenet(행위 주도 개발)
  • 시나리오 기반으로 테스트 코드를 작성하는 개발 방법
  • 하나의 시나리오는 Given, When, Then 구조를 가짐

강의에서 만들 시나리오는 비밀번호 유효성 검증기이다.

요구사항

  • 비밀번호는 최소 8자 이상 12자 이하
  • 8자 미만 || 12자 초과인 경우 IllegalArgumentException 예외 발생
  • 경계조건에 대해 테스트 코드만 작성해야 한다.

@ParameterizedTest

위의 @ParameterizedTest는 따로 공부를 더 해야 할것 같다. 공부한 내용은 추가로 아래에 수정하도록 하겠다.


package org.example;
public class PasswordValidator {
    public static void validate(String password) {
        int length = password.length();
        if(length <8 || length >12){
            throw new IllegalArgumentException("비밀번호는 최소 8자 이상 12자 이하");
        }
    }
}

--

public class PasswordValidatorTest {

    @DisplayName("비밀번호가 최소 8자 이상, 12자 이하면 예외 발생 X")
    @Test
    void validatePasswordTest() {
        assertThatCode(() ->PasswordValidator.validate("serverwizard"))
                .doesNotThrowAnyException();
    }

    @DisplayName("비밀번호가 8자 미만 또는 12자 초과하는 경우 IllegalArgumentException 예외가 발생한다")
    @ParameterizedTest
    @ValueSource(strings = {"aabbcce","aaaaaaaaaaaaa"})
    void validatePasswordTest2(String password) {
        assertThatCode(() -> PasswordValidator.validate(password))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("비밀번호는 최소 8자 이상 12자 이하");
    }
}

참고로 테스트코드와 실행코드는 동일한 패키지 형식을 가져야 효율적이라고 한다.

여기서 객체 지향에 대해서 정확히 알고 가야 할 것 같다.

객체 지향이란?

답은 없지만 자신만의 생각을 정확히 말할 수 있어야 한다.

객체지향의 4가지 특징

  1. 추상화(Abstraction)
  2. 다형성(Polymorphism)
  3. 캡슐화(Encapsulation)
  4. 상속(Inheritance)

객체지향의 5가지 설계 원칙(SOLID)

  1. SRP: Single Responsibility Principle (단일 책임의 원칙)
  2. OCP: Open/Closed Principle (개방 폐쇄의 원칙)
  3. LSP: Liskov's Substitution Principle (리스코프 치환의 원칙)
  4. ISP: Interface Segregation Principle (인터페이스 분리의 원칙)
  5. DIP: Dependency Inversion Principle (의존성 역전의 원칙)

객체지향 패러다임

  • 적절한 객체에게 적절한 책임을 할당하여 서로 메시지를 주고 받으며 협력하도록 하는 것
  • 점점 증가하는 SW 복잡도를 낮추기 위해 객체지향 패러다임 대두

절차지향 VS 객체지향

  • 책임이 한곳에 집중되어 있는 방식 (getter) (절차지향)
  • 책임이 여러 객체로 적절히 분산되어 있는 방식 (객체지향)

어떠한 변화가 있을 때 아주 빠르게 높은 응집도와 낮은 결합도를 가지고 유연하고 빠르게 수정할 수 있어야 한다.

그러면 테스트 코드와 객체지향의 개념을 실습으로 깨닫기 위해서 사칙 연산 계산기를 만들어보았다.

요구사항

  • 간단한 사칙연산을 할 수 있다.
  • 양수로만 계산할 수 있다.
  • 나눗셈에서 0을 나누는 경우 IllegalArgument 예외를 발생시킨다.
  • MVC패턴 기반으로 구성한다.

public class PositiveNumber {
    private static final String ZERO_OR_NEGATIVE_NUMBER_EXCEPTION_MESSAGE = "0 또는 음수를 전달할 수 없습니다.";
    //위의 코드에서 0이나 음수를 썼을 때의 오류메시지를 변수에 저장한다.
    private final int value;

    public PositiveNumber(int value) {
        validate(value);
        this.value = value;
    }

    private void validate(int value) {
        if (isNegativeNumber(value)) {
            throw new IllegalArgumentException(ZERO_OR_NEGATIVE_NUMBER_EXCEPTION_MESSAGE);
        }
    }
    // validate 메서드를 만든다. value값을 매개변수로 넣으면 isNegativeNumber(value)로 <=0인지 확인한다. true이면 IllegalArgumentException을 발생시킨다.
    private boolean isNegativeNumber(int number) {
        return number <= 0;
    }

    public int toInt() {
        return value;
    }
}

public interface ArithmeticOperator {
    boolean supports(String operator);
    int calculate(final PositiveNumber operand1, final PositiveNumber operand2);
}

public class AdditionOperator implements ArithmeticOperator {

    @Override
    public boolean supports(String operator) {
        return "+".equals(operator);
    }
// operator 가 +이면 true
    @Override
    public int calculate(PositiveNumber operand1, PositiveNumber operand2) {
        return operand1.toInt() + operand2.toInt();
    }
    //operand 1,2가 0> 이면 + 실행 <=0 이면 예외 처리
}

public class DivisionOperator implements ArithmeticOperator{
    @Override
    public boolean supports(String operator) {
        return "/".equals(operator);
    }
// operator 가 / 이면 true
    @Override
    public int calculate(PositiveNumber operand1, PositiveNumber operand2) {
        return operand1.toInt() / operand2.toInt();
    }
    //operand 1,2가 0> 이면 / 실행 <=0 이면 예외 처리
}

public enum ArithmeticOperator {
    ADDITION("+") {
        @Override
        public int calculate(final int operand1, final int operand2) {
            return operand1 + operand2;
        }
    },
    SUBTRACTION("-") {
        @Override
        public int calculate(final int operand1, final int operand2) {
            return operand1 - operand2;
        }
    },
    MULTIPLICATION("*") {
        @Override
        public int calculate(final int operand1, final int operand2) {
            return operand1 * operand2;
        }
    }, DIVISION("/") {
        @Override
        public int calculate(final int operand1, final int operand2) {
            if (operand2 == 0) {
                throw new IllegalArgumentException("0으로 나눌 수 없습니다.");
            }
            return operand1 / operand2;
        }
    };

    private final String operator;

    ArithmeticOperator(String operator) {
        this.operator = operator;
    }

    public abstract int calculate(final int operand1, final int operand2);

    public static int calculate(final int operand1, final String operator, final int operand2) {
        ArithmeticOperator selectedArithmeticOperator = Arrays.stream(ArithmeticOperator.values())
                .filter(v -> v.operator.equals(operator))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("올바른 사칙연산이 아닙니다."));

        return selectedArithmeticOperator.calculate(operand1, operand2);
    }
}

위의 코드가 이번 강의에 주요 코드이다. 그래서 난이도가 좀 있다고 생각하고 나는 위에 코드에서 enumstream에 대해서 잘 알지 못한다.

그래서 두 용어에 대해서 간단하게 알아보겠다.

enum의 뜻은 열거형으로 열거 가능한 상수들의 집합을 정의하는 데이터 형식.
주로 프로그래밍에서 사용되며, 특정한 값들의 집합을 정의하고 그 값들을 변수로 사용하게 해준다고 한다.

enum의 특징

  1. 값의 집합 정의: 예를 들어 요일(enum Days{MONDAY,TUESDAY..} 이나 상태(enum State{READY, RUNNING,,} 등을 정의 한다.
  2. 상수의 열거: 예를 들어 요일을 처리하는 코드에서 'Days.MONDAY', 'DAYS.TUESDAY'와 같이 사용한다.
  3. 타입 안정성: 컴파일러가 타입을 확인하여 안정성을 제공한다. 잘못된 상수 값이 사용되는 것을 방지할 수 있다.
  4. 추가적인 값과 메서드 정의: 필요한 경우 값이나 메서드를 정의할 수 있다.

한 마디로 Enum을 사용하는 것은 특정한 선택지를 미리 정의하고, 그 중 하나를 선택하도록 강제하는 것이다.

예를 들어 가게에서는 초코, 바닐라, 딸기 세 가지 맛의 아이스크림만 판매한다. 그럴 경우

// 아이스크림 맛을 정의하는 Enum
public enum IceCreamFlavor {
    CHOCOLATE,
    VANILLA,
    STRAWBERRY
}

// 아이스크림 가게의 고객 클래스
public class Customer {
    public static void main(String[] args) {
        // 고객이 초코 맛을 선택함
        IceCreamFlavor choice = IceCreamFlavor.CHOCOLATE;

        switch (choice) {
            case CHOCOLATE:
                System.out.println("초코 아이스크림을 선택하셨습니다.");
                break;
            case VANILLA:
                System.out.println("바닐라 아이스크림을 선택하셨습니다.");
                break;
            case STRAWBERRY:
                System.out.println("딸기 아이스크림을 선택하셨습니다.");
                break;
        }
    }
}

위와 같이 Enum을 사용할 수 있다. 이를 참고해 보면 계산기 코드에서 Enum함수는 Enum에 따라ㅏ 다른 동작을 구현해야 하기 때문에 calculate메서드를 만든 후에 각 기호마다 다른 동작을 할 수 있도록 오버라이드하여 개별 Enum 상수에 대해 구체적인 동작을 정의한 것이다.
또한 Stream 부분은 ArithmeeticOperator.values()를 사용해서 해당 Enum의 모든 상수를 배열로 반환한 후에 filter를 통해서 배열에 있는 Enum의 상수 'v'중 operator 필드와 같은 v만 남도록 필터링을 하고 .findFirst()로 조건을 만족하는 첫 번째 요소를 찾는다. 만약 필터가 되는 기호가 없다면, IllegalArgumentException을 발생시키는 원리이다.

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CalculatorTest {
    @DisplayName("덧셈 연산을 수행한다")
    @ParameterizedTest
    @MethodSource("formulaAndResult")
    void calculateTest(int operand1, String operator, int operand2, int result) {
        int calculateResult = Calculator.calculate(operand1,operator,operand2);

        assertThat(result).isEqualTo(result);
    }
    private Stream<Arguments> formulaAndResult() {
        return Stream.of(
                arguments(1,"+",2,3),
                arguments(1,"-",2,-1),
                arguments(4,"*",2,8),
                arguments(4,"/",2,2)

        );
    }
}

위와 같은 테스트 코드를 통해서 코드를 계속 리팩토링하면 가장 효율적인 코드를 찾을 수 있게 된다.

다음 예시는 학점계산기를 구현해 보았다.

요구사항은

  • 평균학점 계산 방법 = (학점수 x 교과목 평점)의 합계/수강신청 총학점 수
  • MVC패턴 기반으로 구성
  • 일급 컬렉션을 사용
package org.example.grade.domain;

public class Course {
    public static final int MAJOR_CREDIT = 3;
    public static final int GENERAL_CREDIT = 2;

    private final String subject; // 과목
    private final int credit; // 학점
    private final String grade; // 성적

    public Course(String subject, int credit, String grade) {
        this.subject = subject;
        this.credit = credit;
        this.grade = grade;
    }

    public double multiplyCreditAndCourseGrade() {
        return credit * getGradeToNumber();
    }

    public int getCredit() {
        return this.credit;
    }

    private double getGradeToNumber() {
        double gradeInt = 0;
        switch (this.grade) {
            case "A+":
                gradeInt = 4.5;
                break;
            case "A":
                gradeInt = 4.0;
                break;
            case "B+":
                gradeInt = 3.5;
                break;
            case "B":
                gradeInt = 3.0;
                break;
            case "C+":
                gradeInt = 2.5;
                break;
            case "C":
                gradeInt = 2.0;
                break;
            case "D+":
                gradeInt = 1.5;
                break;
            case "D":
                gradeInt = 1.0;
                break;
            case "F":
                gradeInt = 0.0;
                break;
        }

        return gradeInt;
    }
}

Course 클래스의 간단한 메서드와 생성자이다.

package org.example.grade.domain;

import java.util.List;

public class Courses {
    private final List<Course> courses;

    public Courses(List<Course> courses) {
        this.courses = courses;
    }

    // 학점수×교과목 평점
    public double multiplyCreditAndCourseGrade() {
        return courses.stream()
                .mapToDouble(Course::multiplyCreditAndCourseGrade)
                .sum();
    }

    // 총 이수한 학점
    public int calculateTotalCompletedCredit() {
        return courses.stream()
                .mapToInt(Course::getCredit)
                .sum();
    }
}

stram을 이용해서 Courses 리스트안의 각 요소인 course를 multiplyCreditAndCourseGrade 메서드를 실행시키고 모두 sum을 시킨 후 double로 return한다.

import org.example.grade.domain.Course;
import org.example.grade.domain.Courses;
import org.example.grade.domain.GradeCalculator;
import org.example.grade.domain.GradeResult;
import org.example.grade.ui.ConsoleOutputUI;
import org.junit.jupiter.api.Test;

import java.util.List;

public class GradeCalculatorTest {
    // 학점계산기, 코스
    // 평균학점 계산 요청 ---> '학점계산기' ---> (학점수×교과목 평점)의 합계 ---> '코스'
    //                               ---> 수강신청 총학점 수
    @Test
    void calculateGradeTest() {
        // given
        List<Course> courses = List.of(new Course("OOP", Course.MAJOR_CREDIT, "A+"),
                new Course("자료구조", Course.MAJOR_CREDIT, "A+"),
                new Course("중국어회화", Course.GENERAL_CREDIT, "C"));

        // when
        GradeCalculator gradeCalculator = new GradeCalculator(new Courses(courses));
        GradeResult gradeResult = gradeCalculator.calculateGrade();

        // then
        ConsoleOutputUI.printGrade(gradeResult);
    }
}

위의 코드 역시 테스트 코드를 사용하면 더 편하게 만들 수 있다.

@WebMvcTest(DMakerController.class)
// 내가 지정한 Controller 전용 Test

TDD에 대한 정리

  • 테스트 주도 개발이라는 의미
  • 테스트를 먼저 설계 및 구축 후 테스트를 통과할 수 있게 하는 것

테스트코드를 작성한다면?

  • 코드의 안정성이 높아진다
  • 기능을 추가하거나 변경하는 과정에서 Side-Effect를 줄일 수 있다
  • 해당 코드가 작성된 목적을 명확하게 표현할 수 있다

Junit 라이프 사이클

  1. @Test : 테스트용 메소드를 표현하는 어노테이션
  2. @BeforeEach : 각 테스트 메소드가 시작되기 전에 실행되어야 하는 메소드
  3. @AfterEach : 각 테스트 메소드가 시작된 후 실행되어야 하는 메소드
  4. @BeforeAll : 테스트 시작 전에 실행되어야 하는 메소드(static 처리 필요)
  5. @AfterAll : 테스트 종료 후에 실행되어야 하는 메소드(static 처리 필요)

Junit Main Annotation

  1. @SpringBootTest : 통합 테스트 용도로 사용된다. @SpringBootApplication을 찾아가 하위의 모든 Bean을 스캔하여 로드한다. 그 후 Test용 Application Context를 만들어 Bean을 추가하고, MockBean을 찾아 교체한다.

  2. @ExtendWith : 메인으로 실행될 Class를 지정할 수 있으며, @SpringBootTest는 기본적으로 @ExtendWith가 추가되어 있다

  3. @WebMcvTest(Class명.class) : ()에 작성된 클래스만 실제로 로드하여 테스트를 진행, 매개변수를 지정해주지 않으면 @Controller, @RestController, @RestControllerAdvice 등 컨트롤러와 연관된 Bean이 모두 로드된다. 스프링의 모든 Bean을 로드하는 @SpringBootTest 대신 컨트롤러 관련 코드만 테스트할 경우 사용

  4. @Autowired about MockBean : Contoller의 API를 테스트 하는 용도인 MockMvc 객체를 주입 받는다. perform() 메소드를 활용해 컨트롤러의 동작을 확인할 수있다

  5. @MockBean : 테스트할 클래스에서 주입 받고있는 객체에 대해 가짜 객체를 생성해주는 어노테이션, 해당 객체는 실제 행위를 하지 않으며 given() 메소드를 활용해서 가짜 객체의 동작에 대해 정의하여 사용할 수 있다.

  6. @AutoConfigureMockMvc : spring.test.mockmvc의 설정을 로드하면서 MockMvc의 의존성을 자동으로 주입하며 MockMVC 클래스는 REST API 테스트를 할 수 있는 클래스이다.

  7. @Import : 필요한 Class들을 Configuration으로 만들어 사용할 수 있다

통합 테스트

여러 기능을 조합하여 전체 비즈니스 로직이 제대로 동작하는지 확인하는 것을 의미

단위 테스트

프로젝트에 필요한 모든 기능에 대한 테스트를 각각 진행하는 것을 의미

profile
백엔드 개발자 준비생
post-custom-banner

0개의 댓글