Code Testing With JUnit 2

Sungju Kim·2024년 9월 13일

Sparta_Coding_Camp_TIL

목록 보기
35/53

Given-When-Then style testing

  • Given: Set up the initial conditions for the test. This involves creating any necessary objects or setting the state of the system to a known starting point.
  • When: Perform the action or trigger the event that the test is supposed to verify.
  • Then: Check that the expected outcome or result is achieved.

Mockito: @Mock

Mock is a proxy object (aka fake object) used in testing to make the process timely and efficient.

@ExtendWith(MockitoExtension.class) // @Mock 사용을 위해 설정합니다.
class ProductServiceTest {

    @Mock
    ProductRepository productRepository;

    @Mock
    FolderRepository folderRepository;

    @Mock
    ProductFolderRepository productFolderRepository;

    @Test
    @DisplayName("Change bit price to higher than its minimum")
    void test1() {
        // given
        Long productId = 100L;
        int myprice = ProductService.MIN_MY_PRICE + 3_000_000;

        ProductMypriceRequestDto requestMyPriceDto = new ProductMypriceRequestDto();
        requestMyPriceDto.setMyprice(myprice);

        User user = new User();
        ProductRequestDto requestProductDto = new ProductRequestDto(
                "Apple <b>맥북</b> <b>프로</b> 16형 2021년 <b>M1</b> Max 10코어 실버 (MK1H3KH/A) ",
                "https://shopping-phinf.pstatic.net/main_2941337/29413376619.20220705152340.jpg",
                "https://search.shopping.naver.com/gate.nhn?id=29413376619",
                3515000
        );

        Product product = new Product(requestProductDto, user);

        ProductService productService = new ProductService(productRepository, folderRepository, productFolderRepository);

        given(productRepository.findById(productId)).willReturn(Optional.of(product));

        // when
        ProductResponseDto result = productService.updateProduct(productId, requestMyPriceDto);

        // then
        assertEquals(myprice, result.getMyprice());
    }
}

Types of Tests

Unit Test

A unit test focuses on testing individual components or functions of an application in isolation. The goal is to ensure that each unit of the codebase, such as a class, method, or function, behaves as expected under different conditions.

Integration Test (feat. @SpringBootTest)

An integration test checks how different components or modules of an application work together. These tests focus on verifying the interactions and communication between units (e.g., classes, services, or modules).

End To End Test

An end-to-end (E2E) test validates the entire application workflow from start to finish. These tests simulate real user scenarios and check if the system as a whole behaves as expected.

Testing Service (Integration Testing)

Original Service Class

@Override
public void follow(User user, Long toUserId) {
	Long fromUserId = user.getUserId();
    
    if (Objects.equals(fromUserId, toUserId)) {
    	throw new IllegalArgumentException("You can't follow yourself");
    }
    
    Optional<Follow> savedFollow = followRepository.findByFromUserIdAndToUserId(fromUserId, toUserId);
    if (savedFollow.isEmpty()) {
    	followRepository.saveFollow(fromUserId, toUserId);
    } else {
    	followRepository.deleteFollow(savedFollow.get());
    }
}

Test Code 1: send follow request

@Test
@DisplayName("success: new follow")
void success1() {
  // given
  User user = TEST_USER1;
  Long toUserId = TEST_USER2.getUserId();

  given(followRepository.findByFromUserIdAndToUserId(user.getUserId(), toUserId))
      .willReturn(Optional.empty());

  // when
  userService.follow(suer, toUserId)

  // then: This specifies that you expect the method `saveFollow` on the `followRepository` to be called exactly once.
  // The times(1) is a method in Mockito that allows you to specify how many times the method should be called. 
  // If the method is called more or fewer times, the test will fail.
  then(followRepository).should(time(1)).saveFollow(user.getUserId(), toUserId);
}

Test Code 2: cancel follow request

@Test
@DisplayName("success: follow -> unfollow")
void success2() {
	// given
    User user = TEST_USER1;
    Long toUserId = TEST_USER2.getUserId();

    given(followRepository.findByFromUserIdAndToUserId(user.getUserId(), toUserId))
        .willReturn(Optional.of(new Follow(user.getUserId(), toUserId)));

    // when
    userService.follow(suer, toUserId)

    // then
    then(followRepository).should(time(1)).deleteFollow(any(Follow.class));
}


Test Code 3: follow oneself

@Test
@DisplayName("fail: cannot follow yourself")
void fail () {
	// given
    User user = TEST_USER1;
    Long toUserId = user.getUserId();

}

Testing Controller (Integration Testing)

  • This test is focused on verifying that the CommentController's GET /todos/{todoId}/comments endpoint works correctly.
  • It mocks the CommentService so that the focus remains on the controller.
  • The test sends an HTTP request to retrieve comments and checks that the status of the response is 200 OK.
  • The service layer is mocked to return an empty list of comments, and the test checks that the controller correctly responds with the appropriate HTTP status.

Breakdown of the Test Code:

  • @WebMvcTest(CommentController.class):
    This annotation is used to configure a Spring MVC test for the CommentController class. It starts up only the web layer of the application and focuses on testing the controller. Other parts of the application (like services or repositories) are not loaded, keeping the test lightweight and focused on the controller.

  • @Autowired private MockMvc mockMvc;:
    MockMvc is a Spring utility that allows you to simulate HTTP requests and test the web layer (controllers) of your application without starting a real server. Here, it's autowired to enable interaction with the controller.

  • @MockBean private CommentService commentService;:
    @MockBean is used to create a mock of the CommentService bean. This ensures that during the test, the real CommentService is replaced with a mock, allowing you to simulate behavior (like returning fake data) and control its responses.

  • given(commentService.getComments(anyLong())).willReturn(List.of());:
    This line sets up the mock behavior for the commentService. It says, "When the getComments method is called with any long value (anyLong()), return an empty list (List.of())." This simulates a scenario where the todo item has no comments.

  • mockMvc.perform(get("/todos/{todoId}/comments", todoId));:
    This simulates an HTTP GET request to the /todos/1/comments endpoint. The {todoId} is replaced with the todoId value (1L in this case).

  • ResultActions resultActions:
    The result of performing this request is captured in the ResultActions object. This allows you to perform further assertions on the response, like checking status codes or response bodies.

  • resultActions.andExpect(status().isOk());:
    This verifies that the response from the controller has a status of 200 OK. This assertion ensures that the request was successful, indicating the controller correctly handled the request.

package org.example.expert.domain.comment.service.controller;

import org.example.expert.domain.comment.controller.CommentController;
import org.example.expert.domain.comment.service.CommentService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import java.util.List;

import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(CommentController.class)
public class CommentControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private CommentService commentService;

    @Test
    public void get_comments() throws Exception {
        // given
        long todoId = 1L;

        given(commentService.getComments(anyLong())).willReturn(List.of());

        // when
        ResultActions resultActions = mockMvc.perform(get("/todos/{todoId}/comments", todoId));

        // then
        resultActions.andExpect(status().isOk());
    }
}

Testing Repository (Integration Testing)

@DataJpaTest // loads beans related to the repository layer
public class PasswordHistoryRepositoryTest implements UserFixture {
      
      // The test will verify the behavior of the PasswordHistoryRepository, 
      // while UserRepository is used to persist a user to whom the 
      // password history belongs.
      @Autowired
      PasswordHistoryRepository passwordHistoryRepository;
      @Autowired
      UserRepository userRepository;
      
      @DisplayName("GET: three recent passwords of a user")
      @Test
      @Disabled
      void find Top3ByUserId() {
            // given
            var passwordHistory1 = MyPageTestUtils.get(PASSWORD_HISTORY1, id: 1L,
                PASSWORD_HISTORY1.getCreatedAt(),
                TEST_REPOSITORY_USER);
            
            var passwordHistory2 = MyPageTestUtils.get(PASSWORD_HISTORY2, id: 2L,
                PASSWORD_HISTORY2.getCreatedAt(),
                TEST_REPOSITORY_USER);
            
            var passwordHistory3 = MyPageTestUtils.get(PASSWORD_HISTORY3, id: 3L,
            	PASSWORD_HISTORY3.getCreatedAt(),
            	TEST_REPOSITORY_USER);
            
            userRepository.save(TEST_REPOSITORY_USER);
            passwordHistoryRepository.save(passwordHistory1)
            passwordHistoryRepository.save(passwordHistory2);
            passwordHistoryRepository.save(passwordHistory3);
      
            // when
            var result = passwordHistoryRepository.findTop3ByUserIdOrderByCreatedAtDesc(1L);
      
            // then
            // The first assertion checks that the second password history (result.get(1))
            // was created before the first one (result.get(0)), confirming that the results 
            // are correctly ordered by createdAt in descending order.
            assertThat(result.get(1).getCreatedAt()).isBefore(result.get(0).getCreatedAt());
            assertThat(result.size()).isEqualTo( expected: 3);
      }
}
profile
Fully ✨committed✨ developer, always eager to learn!

1개의 댓글

comment-user-thumbnail
2024년 10월 22일

Really useful. These test programs can show us the progress of completion. Besides, learn and understand more about the coreball rules to use the appropriate language. The knowledge is constantly improving to reach more together.

답글 달기