본문 바로가기
Project

[Toss] 중복거래 방지기능 (2)

by sangyunpark 2023. 8. 30.

기능 구현은 완료했으니, 테스트 코드를 작성하자

 

LockAopAspectTest

package com.example.account.service;

import com.example.account.dto.UseBalance;
import com.example.account.exception.AccountException;
import com.example.account.type.ErrorCode;
import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
class LockAopAspectTest {
    @Mock
    private LockService lockService;

    @Mock
    private ProceedingJoinPoint proceedingJoinPoint;

    @InjectMocks
    private LockAopAspect lockAopAspect;

    @Test
    @DisplayName("Lock과 unLock이 잘 수행되는가")
    void lockAndUnlock() throws Throwable {
        //given
        // 계좌를 확인하기 위해서는 argumentCaptor를 사용한다.
        ArgumentCaptor<String> lockArgumentCaptor = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor<String> unlockArgumentCaptor = ArgumentCaptor.forClass(String.class);

        UseBalance.Request request = new UseBalance.Request(1L,"1000000000",1000L);

        //when
        lockAopAspect.aroundMethod(proceedingJoinPoint, request);

        //then
        verify(lockService, times(1)).lock(lockArgumentCaptor.capture());
        verify(lockService, times(1)).unlock(unlockArgumentCaptor.capture());

        assertEquals("1000000000", lockArgumentCaptor.getValue());
        assertEquals("1000000000", unlockArgumentCaptor.getValue());
    }

    @Test
    @DisplayName("excepiton이 발생하더라도 Lock과 unLock이 잘 수행되는가")
    void lockAndUnlock_evenIfThrow() throws Throwable {
        //given
        // 계좌를 확인하기 위해서는 argumentCaptor를 사용한다.
        ArgumentCaptor<String> lockArgumentCaptor = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor<String> unlockArgumentCaptor = ArgumentCaptor.forClass(String.class);

        UseBalance.Request request = new UseBalance.Request(1L,"1000000000",1000L);

        given(proceedingJoinPoint.proceed()) // 에러 던져주기
                .willThrow(new AccountException(ErrorCode.ACCOUNT_NOT_FOUND));

        //when
        assertThrows(AccountException.class, () -> lockAopAspect.aroundMethod(proceedingJoinPoint, request));

        //then
        verify(lockService, times(1)).lock(lockArgumentCaptor.capture());
        verify(lockService, times(1)).unlock(unlockArgumentCaptor.capture());

        assertEquals("1000000000", lockArgumentCaptor.getValue());
        assertEquals("1000000000", unlockArgumentCaptor.getValue());
    }

}

 

LockServiceTest

package com.example.account.service;

import com.example.account.exception.AccountException;
import com.example.account.type.ErrorCode;
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.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.given;

@ExtendWith(MockitoExtension.class)
class LockServiceTest {
    @Mock
    private RedissonClient redissonClient;

    @Mock
    private RLock rLock;

    @InjectMocks
    private LockService lockService;

    @Test
    @DisplayName("Lock 취득 성공")
    void successGetLock() throws Exception{
        //given
        given(redissonClient.getLock(anyString()))
                .willReturn(rLock);

        given(rLock.tryLock(anyLong(), anyLong(), any()))
                .willReturn(true);

        //when
        assertDoesNotThrow(() -> lockService.lock("123")); // lock을 취득하면 아무런 오류 발생 x
    }

    @Test
    @DisplayName("Lock 취득 실패")
    void failGetLock() throws Exception{
        //given
        given(redissonClient.getLock(anyString()))
                .willReturn(rLock);

        given(rLock.tryLock(anyLong(),anyLong(),any()))
                .willReturn(false);
        //when
        AccountException exception = assertThrows(AccountException.class, () -> lockService.lock("123"));

        //then
        assertEquals(exception.getErrorCode(), ErrorCode.ACCOUNT_TRANSACTION_LOCK);
    }
}

 

'Project' 카테고리의 다른 글

[Weather Diary] JDBC 방식으로 데이터 저장  (0) 2023.09.01
[Toss] 리팩토링  (0) 2023.08.30
[Toss] 중복거래 방지 기능  (0) 2023.08.29
[Toss] 예외처리 수정  (0) 2023.08.28
[Toss] 잔액사용 확인(2)  (0) 2023.08.26