ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JUnit] Spring Boot 애플리케이션 테스트
    DEV/JUnit 2024. 12. 10. 17:33

    Spring Initializr 를 이용해 Spring 프로젝트 만들기

    https://start.spring.io/

    해당 페이지에서 스프링을 구동하기 위한 최소한의 설정을 가지고 스프링 부트 프로젝트 뼈대를 만들어줌

    - Explore : 생성될 프로젝트에 대한 세부정보 확인 가능

    - Generate : 프로젝트가 포함된 zip파일 다운로드

     

    JUnit5로 테스트한 Spring 애플리케이션을 Spring Boot로 전환하기

    예제 코드 : https://github.com/devkunst/junit-in-action-third-edition-kr/tree/master/ch17-spring-boot/ch17-spring-boot-initializr-old-feature

     

    1. 패키지 구조 설정

    - model :  모델 클래스 만들기

    - registration : 승객 등록과 관련된 클래스 만들기

     

    2. application-context.xml 파일의 컴포넌트 스캔 설정 제거

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="passenger" class="com.manning.junitbook.spring_boot.model.Passenger">
            <constructor-arg name="name" value="John Smith"/>
            <property name="country" ref="country"/>
            <property name="isRegistered" value="false"/>
        </bean>
    
        <bean id="country" class="com.manning.junitbook.spring_boot.model.Country">
            <constructor-arg name="name" value="USA"/>
            <constructor-arg name="codeName" value="US"/>
        </bean>
    
    </beans>

     

     <context:component-scan base-package=".." /> 대신 애노테이션 사용

     

    @SpringBootTest
    @EnableAutoConfiguration
    @ImportResource("classpath:application-context.xml")
    class RegistrationTest {
    
    	@Autowired
    	private Passenger passenger;
    
    	@Autowired
    	private RegistrationManager registrationManager;
    
    	@Test
    	void testPersonRegistration() {
    		registrationManager.getApplicationContext().publishEvent(new PassengerRegistrationEvent(passenger));
    		System.out.println("After registering:");
    		System.out.println(passenger);
    		assertTrue(passenger.isRegistered());
    	}
    
    }

    - @SpringBootTest : @EnableAutoConfiguration 애노테이션과 함께 현재 테스트 클래스 패키지와 서브 패키지에서 스프링 빈을 검색한다. 해당 애노테이션이 빈을 알아서 찾아 @Autowired (객체 주입)이 가능하다

    - @ImportResource : XML 설정에 여전히 데이터 빈이 존재할 경우 설정파일로부터 빈을 가져오는 애노테이션

     

    실행결과 (성공)

     

    Spring Boot에서 테스트 전용 구성 구현하기

    xml은 전통적인 스프링 빈 구성 방식으로 외부 의존성이 필요하지 않으며 소스 코드를 다시 컴파일할 필요가 없어 바꾸기 쉽다. 그러나 Spring Boot에서 제공하는 애노테이션을 사용하여 이를 대체한다면 Java 오류를 사전에 방지할 수 있다. 설정이 잘못 되었을 경우 컴파일 오류를 띄우기 떄문에, Java 기반으로 정의된 Bean은 타입에 안전하다.

     

    1. application-context.xml 의 기존 Bean을 @TestConfiguration 애노테이션으로 대체하기

    package com.manning.junitbook.spring_boot.beans;
    
    import com.manning.junitbook.spring_boot.model.Country;
    import com.manning.junitbook.spring_boot.model.Passenger;
    import org.springframework.boot.test.context.TestConfiguration;
    import org.springframework.context.annotation.Bean;
    
    @TestConfiguration
    public class TestBeans {
    
        @Bean
        Passenger createPassenger() {
            Passenger passenger = new Passenger("John Smith");
            passenger.setCountry(createCountry());
            passenger.setIsRegistered(false);
            return passenger;
        }
    
        @Bean
        Country createCountry() {
            Country country = new Country("USA", "US");
            return country;
        }
    }

    - @TestConfiguration : 빈을 정의하거나 테스트에 적용하기 위한 사용자 정의 구성에 사용할 수 있다.

    - @Bean 애노테이션을 통해 해당 빈을 사용할 것을 명시

    - RegistrationManager와 PassengerRegistrationListener 같은 기능 빈은 @Service로 등록한다.

    - 기존의 applicaition-context.xml 은 삭제

     

    2. RegistrationTest 클래스 수정

    @SpringBootTest
    @Import(TestBeans.class)
    class RegistrationTest {
    ...
    }

    - @EnableAutoConfiguration, @ImportResource로 빈을 가져오던 방식을 @Import 애노테이션으로 변경

     

    실행결과 (동일하게 성공)

     

    신규 기능 추가하여 Spring Boot 애플리케이션 테스트하기

    : 항공편 생성 및 설정, 항공편에 승객 추가 및 삭제 기능 추가

     

    1.  Flight 클래스 생성

    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    public class Flight {
    
        private String flightNumber;
        private int seats;
        private Set<Passenger> passengers = new HashSet<>();
    
        public Flight(String flightNumber, int seats) {
            this.flightNumber = flightNumber;
            this.seats = seats;
        }
    
        public String getFlightNumber() {
            return flightNumber;
        }
    
        public int getSeats() {
            return seats;
        }
    
        public Set<Passenger> getPassengers() {
            return Collections.unmodifiableSet(passengers);
        }
    
        public boolean addPassenger(Passenger passenger) {
            if (passengers.size() >= seats) {
                throw new RuntimeException("항공편의 좌석 수보다 더 많은 승객을 추가할 수 없습니다");
            }
            return passengers.add(passenger);
        }
    
        public boolean removePassenger(Passenger passenger) {
            return passengers.remove(passenger);
        }
    
        @Override
        public String toString() {
            return "Flight " + getFlightNumber();
        }
    
    }

    - getPassenger() : unmodifiableSet을 사용하여 수정할 수 없는 승객 정보 리스트를 반환

     

    2. 승객 리스트 CSV 생성

    flights_infromation.csv

    John Smith; UK
    Jane Underwood; AU
    James Perkins; US
    Mary Calderon; US
    Noah Graves; UK
    Jake Chavez; AU
    Oliver Aguilar; US
    Emma McCann; AU
    Margaret Knight; US
    Amelia Curry; UK
    Jack Vaughn; US
    Liam Lewis; AU
    Olivia Reyes; US
    Samantha Poole; AU
    Patricia Jordan; UK
    Robert Sherman; US
    Mason Burton; AU
    Harry Christensen; UK
    Jennifer Mills; US
    Sophia Graham; UK

     

    3. FlightBuilder 클래스 생성

    : CSV 파일을 읽어와 항공편에 승객 정보를 채워 넣음

    import com.manning.junitbook.spring_boot.model.Country;
    import com.manning.junitbook.spring_boot.model.Flight;
    import com.manning.junitbook.spring_boot.model.Passenger;
    import org.springframework.boot.test.context.TestConfiguration;
    import org.springframework.context.annotation.Bean;
    
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    @TestConfiguration
    public class FlightBuilder {
    
        private static Map<String, Country> countriesMap = new HashMap<>();
    
        static {
            countriesMap.put("AU", new Country("Australia", "AU"));
            countriesMap.put("US", new Country("USA", "US"));
            countriesMap.put("UK", new Country("United Kingdom", "UK"));
        }
    
        @Bean
        Flight buildFlightFromCsv() throws IOException {
            Flight flight = new Flight("AA1234", 20);
            try (BufferedReader reader = new BufferedReader(new FileReader("src/test/resources/flights_information.csv"))) {
                String line = null;
                do {
                    line = reader.readLine();
                    if (line != null) {
                        String[] passengerString = line.toString().split(";");
                        Passenger passenger = new Passenger(passengerString[0].trim());
                        passenger.setCountry(countriesMap.get(passengerString[1].trim()));
                        passenger.setIsRegistered(false); // 승객을 미등록 상태로 설정
                        flight.addPassenger(passenger); // 항공편에 승객 추가
                    }
                } while (line != null);
    
            }
    
            return flight;
        }
    }

     

    4. FlightTest 클래스 생성

    : 항공편에 추가된 모든 승객을 등록하고 등록이 완료되었는지 테스트 검증

    import com.manning.junitbook.spring_boot.beans.FlightBuilder;
    import com.manning.junitbook.spring_boot.model.Flight;
    import com.manning.junitbook.spring_boot.model.Passenger;
    import com.manning.junitbook.spring_boot.registration.PassengerRegistrationEvent;
    import com.manning.junitbook.spring_boot.registration.RegistrationManager;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.context.annotation.Import;
    
    import static org.junit.jupiter.api.Assertions.assertFalse;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    @SpringBootTest
    @Import(FlightBuilder.class)
    public class FlightTest {
    
        @Autowired
        private Flight flight;
    
        @Autowired
        private RegistrationManager registrationManager;
    
        @Test
        void testFlightPassengersRegistration() {
            for (Passenger passenger : flight.getPassengers()) {
                assertFalse(passenger.isRegistered());
                registrationManager.getApplicationContext().publishEvent(new PassengerRegistrationEvent(passenger));
            }
    
            System.out.println("모든 승객이 등록되었는지 확인");
    
            for (Passenger passenger : flight.getPassengers()) {
                assertTrue(passenger.isRegistered());
            }
        }
    }

     

    실행결과

     

    댓글

Designed by Tistory.