Java/Spring

스프링 쇼핑몰 장바구니 기능 테스트코드(Junit5 단위 테스트)

SeungbeomKim 2022. 9. 18. 17:49

CartControllerTest

package com.example.shoppingmall.controller;

import com.example.shoppingmall.controller.cart.CartController;
import com.example.shoppingmall.dto.cart.CartCreateRequestDto;
import com.example.shoppingmall.entity.member.Member;
import com.example.shoppingmall.repository.member.MemberRepository;
import com.example.shoppingmall.service.cart.CartService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import java.util.Collections;
import java.util.Optional;

import static com.example.shoppingmall.factory.MemberFactory.createUser;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@ExtendWith(MockitoExtension.class)
public class CartControllerUnitTest {

    @InjectMocks
    CartController cartController;

    @Mock
    CartService cartService;

    @Mock
    MemberRepository memberRepository;

    MockMvc mockMvc;
    ObjectMapper objectMapper = new ObjectMapper(); // requestBody

    @BeforeEach
    void beforeEach() {
        mockMvc = MockMvcBuilders.standaloneSetup(cartController).build();
    }

    @Test
    @DisplayName("장바구니 등록 테스트")
    public void createCartTest() throws Exception {
        //given
        CartCreateRequestDto req = new CartCreateRequestDto(1L,1);

        Member member = createUser();
        Authentication authentication = new UsernamePasswordAuthenticationToken(member.getId(),"", Collections.emptyList());
        SecurityContextHolder.getContext().setAuthentication(authentication);

        given(memberRepository.findByUsername(authentication.getName())).willReturn(Optional.of(member));
        //when

        mockMvc.perform(
                post("/api/carts")
                        .content(objectMapper.writeValueAsString(req))
                        .contentType(MediaType.APPLICATION_JSON)
        ).andExpect(status().isCreated());

        //then
        verify(cartService).create(req, member);
    }

    @Test
    @DisplayName("장바구니 조회 테스트")
    public void findAllTest() throws Exception {
        //given x
        Member member = createUser();
        Authentication authentication = new UsernamePasswordAuthenticationToken(member.getId(),"", Collections.emptyList());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        given(memberRepository.findByUsername(authentication.getName())).willReturn(Optional.of(member));

        //when
        mockMvc.perform(
                get("/api/carts")
        ).andExpect(status().isOk());

        //then
        verify(cartService).findAll(member);
    }

    @Test
    @DisplayName("장바구니 품목 삭제 테스트")
    public void deleteTest() throws Exception {
        //given
        Long id = 1L;
        Member member = createUser();
        Authentication authentication = new UsernamePasswordAuthenticationToken(member.getId(),"", Collections.emptyList());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        given(memberRepository.findByUsername(authentication.getName())).willReturn(Optional.of(member));

        //when(실행)
        mockMvc.perform(
                delete("/api/carts/{id}",id)
        ).andExpect(status().isOk());

        //then(검증)
        verify(cartService).deleteById(id, member);
    }

    @Test
    @DisplayName("장바구니 물건 전체 구매 테스트")
    public void buyingTest() throws Exception {
        //given
        Member member = createUser();
        Authentication authentication = new UsernamePasswordAuthenticationToken(member.getId(),"", Collections.emptyList());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        given(memberRepository.findByUsername(authentication.getName())).willReturn(Optional.of(member));

        //when
        mockMvc.perform(
                post("/api/carts/buying")
        ).andExpect(status().isOk());

        //then
        verify(cartService).buyingAll(member);
    }
}

given => 각 Repository에 있는 데이터를 가져와야 하는데, 그 이유는 Mockito에 데이터베이스가 없기 때문이다. 

Optional을 사용하는 이유는 null값이 주어질 수 있는데, 테스트 코드에서는 의도하는 경우를 다루고 다른 예외를 다루지 않기 때문에 Optional.of 를 사용하면서 null 값의 상황을 해결해준다.

given(주어진 값, 매개변수)    

when(실행) =>url요청에 대한 실행을 담당

then(검증)

package com.example.shoppingmall.service;

import com.example.shoppingmall.dto.cart.CartCreateRequestDto;
import com.example.shoppingmall.dto.cart.CartItemResponseDto;
import com.example.shoppingmall.entity.cart.Cart;
import com.example.shoppingmall.entity.cartItem.CartItem;
import com.example.shoppingmall.entity.member.Member;
import com.example.shoppingmall.entity.product.Product;
import com.example.shoppingmall.repository.cart.CartItemRepository;
import com.example.shoppingmall.repository.cart.CartRepository;
import com.example.shoppingmall.repository.product.ProductRepository;
import com.example.shoppingmall.service.cart.CartService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.example.shoppingmall.factory.CartFactory.createCart;
import static com.example.shoppingmall.factory.CartFactory.createCartItem;
import static com.example.shoppingmall.factory.MemberFactory.createSeller;
import static com.example.shoppingmall.factory.MemberFactory.createUser;
import static com.example.shoppingmall.factory.ProductFactory.createProduct;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
public class CartServiceUnitTest {

    @InjectMocks
    CartService cartService;

    @Mock
    CartRepository cartRepository;

    @Mock
    CartItemRepository cartItemRepository;

    @Mock
    ProductRepository productRepository;

    @Test
    @DisplayName("장바구니 등록 테스트 (카트가 있는 경우)")
    public void createCartTest() {
        //given
        CartCreateRequestDto req = new CartCreateRequestDto(1L,1);
        Member member = createUser();
        Member seller = createSeller();
        member.setMoney(100000);
        Cart cart = createCart(member);
        Product product = createProduct(seller);

        given(productRepository.findById(req.getProduct_id())).willReturn(Optional.of(product));
        given(cartRepository.findCartByMember(member)).willReturn(Optional.of(cart));
        given(cartRepository.findCartByMember(member)).willReturn(Optional.of(cart));

        // when
        cartService.create(req, member);

        // then
        verify(cartItemRepository).save(any());
    }

    @Test
    @DisplayName("장바구니 등록 테스트 (카트가 없는 경우)")
    public void createCartEmptyCartTest() {
        //given
        CartCreateRequestDto req = new CartCreateRequestDto(1L,1);
        Member member = createUser();
        Member seller = createSeller();
        member.setMoney(100000);
        Cart cart = createCart(member);
        Product product = createProduct(seller);

        given(productRepository.findById(req.getProduct_id())).willReturn(Optional.of(product));
        given(cartRepository.findCartByMember(member)).willReturn(Optional.empty());
        given(cartRepository.findCartByMember(member)).willReturn(Optional.of(cart));

        // when
        cartService.create(req, member);

        // then
        verify(cartItemRepository).save(any());
    }

    @Test
    @DisplayName("장바구니 조회 테스트")
    public void findAllTest() {
        //given
       Member member = createUser();
       Cart cart = createCart(member);
       given(cartRepository.findCartByMember(member)).willReturn(Optional.of(cart));

       List<CartItem> items = List.of(createCartItem(cart, createProduct(createSeller()), 1));
       given(cartItemRepository.findAllByCart(cart)).willReturn(items);

        //when
        List<CartItemResponseDto> result = cartService.findAll(member);

        //then assertThat : 반환값 o
        //     verify : 반환값 x
        assertThat(result.size()).isEqualTo(1);
    }

    @Test
    @DisplayName("장바구니 삭제 테스트")
    public void deleteTest() {
        //given
        Member member = createUser();
        Long id = 1L;
        CartItem cartItem = createCartItem(createCart(member),createProduct(createSeller()),1);
        given(cartItemRepository.findById(id)).willReturn(Optional.of(cartItem));

        //when
        cartService.deleteById(id, member);

        //then
        verify(cartItemRepository).delete(any());
    }

    @Test
    @DisplayName("전체 구매 테스트")
    public void buyingAllTest() {
        // given
        Member member = createUser();
        Member seller = createSeller();
        seller.setMoney(0);
        member.setMoney(100000000);
        Cart cart = createCart(member);
        given(cartRepository.findCartByMember(member)).willReturn(Optional.of(cart));
        List<CartItem> cartItems = List.of(createCartItem(cart, createProduct(seller), 1));
        given(cartItemRepository.findAllByCart(cart)).willReturn(cartItems);


        // when
        cartService.buyingAll(member);

        // then
        verify(cartRepository).delete(any());
    }

}

List.of  => 선언과 동시에 List 안에 주어진 데이터를 채워주기 위함이다.

factory => 선언과 동시에 초기화를 해주기 위함이다

package com.example.shoppingmall.factory;

import com.example.shoppingmall.entity.cart.Cart;
import com.example.shoppingmall.entity.cartItem.CartItem;
import com.example.shoppingmall.entity.member.Member;
import com.example.shoppingmall.entity.product.Product;

public class CartFactory {

    public static Cart createCart(Member member) {
        return new Cart(member);
    }

    public static CartItem createCartItem(Cart cart, Product product, int quantity) {
        return new CartItem(cart, product, quantity);
    }

}

객체에 있는 생성자를, 팩토리에서 미리 선언해주고 테스트코드에서 빠르게 쓸 수 있다. 

Cart cart = createCart(member);