JUnit & Mockito

EunBeen Noh·2024년 4월 9일
0

SpringAdvanced

목록 보기
2/6
post-thumbnail

1. JUnit

  • Java를 사용한 단위 테스트 프레임워크
  • 보통 Maven, Gradle 등의 빌드 도구와 함께 사용
  • CI/CD 파이프라인에서 자동화된 테스트를 실행하는 데 널리 사용

JUnit 주요 어노테이션

@Test

  • 테스트의 대상(메소드)를 지정할 때 사용

@Before

  • 각 테스트 메소드가 실행되기 전에 실행될 메소드를 지정할 때 사용
  • 테스트 메소드가 실행되기 전에 초기화 작업이나 공통 설정을 수행하는 데 사용

@After

  • 각 테스트 메소드가 실행된 후에 실행될 메소드를 지정할 때 사용
  • 테스트 메소드 실행 후에 정리 작업이나 자원 해제 등을 수행하는 데 사용
  • @Before와 반대로 테스트 메소드 실행 후에 필요한 정리 작업 수행 가능

@BeforeClass

  • 클래스 내의 모든 테스트 메소드가 실행되기 전에 단 한 번만 실행되는 메소드를 지정할 때 사용
  • 주로 클래스 수준의 설정이나 초기화 작업을 수행하는 데 사용
  • static으로 선언되어야 하며, 모든 테스트 메소드보다 먼저 실행

예시 코드

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class CalculatorTest {
    
    private Calculator calculator;
    
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        // 테스트 클래스 내의 모든 테스트 메소드가 실행되기 전에 한 번만 실행
        System.out.println("BeforeClass - setUpBeforeClass");
    }
    
    @Before
    public void setUp() throws Exception {
        // 각 테스트 메소드가 실행되기 전에 실행
        calculator = new Calculator();
        System.out.println("Before - setUp");
    }
    
    @Test
    public void testAdd() {
        // 테스트 케이스: 덧셈 테스트
        int result = calculator.add(3, 5);
        assertEquals(8, result);
        System.out.println("Test - testAdd");
    }
    
    @Test
    public void testSubtract() {
        // 테스트 케이스: 뺄셈 테스트
        int result = calculator.subtract(10, 4);
        assertEquals(6, result);
        System.out.println("Test - testSubtract");
    }
    
    @After
    public void tearDown() throws Exception {
        // 각 테스트 메소드가 실행된 후에 실행
        System.out.println("After - tearDown");
    }
    
    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        // 테스트 클래스 내의 모든 테스트 메소드가 실행된 후에 한 번만 실행
        System.out.println("AfterClass - tearDownAfterClass");
    }
}

2. Mockito

  • Java에서 mocking을 위한 오픈 소스 프레임워크
  • mock(모의) 객체를 생성하고 테스트 중에 실제 객체를 대신하여 사용할 수 있도록 함.
  • Mockito를 사용하면 특정 메소드의 반환 값을 설정하거나 메소드 호출을 확인하는 등의 작업을 수행할 수 있다.

Mockito의 주요 기능

when()

  • 모의 객체의 동작을 지정하기 위해 사용
  • 주로 mock 객체의 메소드가 호출될 때 어떤 값을 반환할지 또는 예외를 발생시킬지를 지정하는 데에 사용
  • thenReturn(), thenThrow() 등과 함께 사용하여 모의 객체의 동작을 명시
    예)when(mockObject.method()).thenReturn(value)와 같은 형태로 사용되며, mockObject의 method()가 호출될 때 value를 반환하도록 지정

thenReturn()

  • when() 메소드와 함께 사용되어 mock 객체의 메소드 호출 시 반환할 값을 지정
  • 주로 메소드가 호출될 때 반환되어야 하는 값을 명시하는 데 사용
  • 다양한 타입의 값을 반환할 수 있으며, 메소드 호출마다 다른 값을 반환할 수도 있다.
    예) when(mockObject.method()).thenReturn(value)와 같이 사용되며, mockObject의 method()가 호출될 때 value를 반환하도록 지정

verify()

  • verify() 메소드는 Mockito에서 mock 객체가 특정 메소드 호출을 받았는지를 검증하기 위해 사용
  • 주로 테스트에서 특정 메소드가 예상대로 호출되었는지를 확인하는 데에 사용
  • 호출 횟수를 지정하거나, 호출되지 않았음을 확인하는 등 다양한 검증 기능을 제공
    예) verify(mockObject).method(argument)와 같은 형태로 사용되며, mockObject가 method() 메소드를 argument와 함께 호출했는지를 검증

mock 객체?

mock 객체는 실제 객체와 같은 동작을 가질 수 있지만 실제 동작을 실행하지는 않는 대신, 테스트 시나리오를 제어하고 의존성을 분리하여 테스트를 더 효율적으로 만들 수 있다.

예제 코드

  • UserService 클래스의 getUserName() 메소드를 테스트하는 예시
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;

public class UserServiceTest {

    @Test
    public void testGetUserName() {
        // 모의 객체 생성
        UserDatabase mockDatabase = mock(UserDatabase.class);
        
        // 모의 객체의 동작 설정
        when(mockDatabase.getUserName(1)).thenReturn("John Doe");
        
        // UserService에 모의 객체 주입
        UserService userService = new UserService(mockDatabase);
        
        // UserService의 메소드 호출 및 결과 검증
        assertEquals("John Doe", userService.getUserName(1));
    }
}

3. JUnit / Mockito 코드 비교

JUnit

@SpringBootTest
class SpringAdvancedApplicationTests {

    @Test
    void contextLoads() {
        // Spring Boot 애플리케이션 컨텍스트 로드 테스트
    }

    @Test
    public void testFindUserById() {
        // Given
        UserRepository userRepository = new TestUserRepository();
        UserService userService = new UserService(userRepository);
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        user.setName("하재훈");
        user.setEmail("haman@example.com");
        userRepository.save(user); // 테스트용 UserRepository에 사용자 추가

        // When
        User foundUser = userService.findUserById(userId);

        // Then
        Assertions.assertNotNull(foundUser);
        Assertions.assertEquals(userId, foundUser.getId());
        Assertions.assertEquals("하재훈", foundUser.getName());
        Assertions.assertEquals("haman@example.com", foundUser.getEmail());
    }

    // 테스트용 UserRepository 구현
    private static class TestUserRepository implements UserRepository {
        private Map<Long, User> users = new HashMap<>();

        @Override
        public Optional<User> findById(Long id) {
            return Optional.ofNullable(users.get(id));
        }

        @Override
        public void save(User user) {
            users.put(user.getId(), user);
        }
    }
}

Mockito

@SpringBootTest
class SpringAdvancedApplicationTests {

    @Test
    void contextLoads() {
        // Spring Boot 애플리케이션 컨텍스트 로드 테스트
    }

    @Test
    public void testFindUserById() {
        // Given
        UserRepository userRepository = Mockito.mock(UserRepository.class);
        UserService userService = new UserService(userRepository);
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        user.setName("하재훈");
        user.setEmail("haman@example.com");
        Mockito.when(userRepository.findById(userId)).thenReturn(Optional.of(user));

        // When
        User foundUser = userService.findUserById(userId);

        // Then
        Assertions.assertNotNull(foundUser);
        Assertions.assertEquals(userId, foundUser.getId());
        Assertions.assertEquals("하재훈", foundUser.getName());
        Assertions.assertEquals("haman@example.com", foundUser.getEmail());
    }
}
  • JUnit 사용 코드에서는 UserRepository를 인터페이스로 만들고, 그것을 구현하는 테스트용 클래스를 추가하여 직접 테스트 데이터를 관리한다.
  • 반면, Mockito를 사용한 버전에서는 Mockito를 사용하여 UserRepository가 mock를 생성하여 테스트하기 때문에 따로 테스트용 UserRepository를 구현할 필요가 없다.

0개의 댓글