티스토리 뷰

현재 작업 : 만들어진 프로젝트를 AWS에 올리는 작업 진행중.

 

로컬에서의 작업이 완료 되고, 서버에 업로드 중입니다.

이전 작업물에서도, 로컬에서는 정상적으로 진행되던 테스트가 에러를 뿜었고,

테스트가 제대로 끝나지 않으면 TDD형식 개발이라고 부를 수 없는 물건이 되어버리기에,

제대로 고치기로 마음 먹고 EC2의 로그를 살펴보았습니다.

 

 

> Task :test

SmApplicationTests > contextLoads() FAILED
    java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
        Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
            Caused by: org.springframework.beans.factory.BeanCreationException at AutowiredAnnotationBeanPostProcessor.java:405
                Caused by: java.lang.IllegalArgumentException at PropertyPlaceholderHelper.java:180

ApplicationTests > contextLoads() 로 구글링을 했을 때는 너무 많은 경우가 나와서, 

하루정도를 삽질하면서 수정을 한 거 같습니다.

원인은 해결되지 않고, 여전히 그 에러가 나오는 중..

 

여기서, 제일 아랫 줄의 

Caused by: java.lang.IllegalArgumentException at PropertyPlaceholderHelper.java:180

를 다시 보면서, 이게 뭐지? 싶어서 검색을 했더니, application.perproties 관련 에러라고 하더군요.

역시 명확한 해결 방법은 없는 상태.

 

그래서 조금 더 aws의 로그를 뒤져보기로 했습니다.

./gradlew test -i

i옵션을 달아서 모든 로그를 띄우게 만들고, 다시 테스트를 돌려보니 에러의 전말이 드러났습니다..

 

 

 Caused by:
            org.springframework.beans.factory.BeanCreationException: Error creati ng bean with name 'twitterService': Injection of autowired dependencies failed; n ested exception is java.lang.IllegalArgumentException: Could not resolve placehol der 'twitter.apiKey' in value "${twitter.apiKey}"
                at org.springframework.beans.factory.annotation.AutowiredAnnotati onBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.ja va:405)
                at org.springframework.beans.factory.support.AbstractAutowireCapa bleBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
                at org.springframework.beans.factory.support.AbstractAutowireCapa bleBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
                at org.springframework.beans.factory.support.AbstractAutowireCapa bleBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
                at org.springframework.beans.factory.support.AbstractBeanFactory. lambda$doGetBean$0(AbstractBeanFactory.java:335)
                at org.springframework.beans.factory.support.DefaultSingletonBean Registry.getSingleton(DefaultSingletonBeanRegistry.java:234)
                at org.springframework.beans.factory.support.AbstractBeanFactory. doGetBean(AbstractBeanFactory.java:333)
                at org.springframework.beans.factory.support.AbstractBeanFactory. getBean(AbstractBeanFactory.java:208)
                at org.springframework.beans.factory.config.DependencyDescriptor. resolveCandidate(DependencyDescriptor.java:276)
                at org.springframework.beans.factory.support.DefaultListableBeanF actory.doResolveDependency(DefaultListableBeanFactory.java:1380)
                at org.springframework.beans.factory.support.DefaultListableBeanF actory.resolveDependency(DefaultListableBeanFactory.java:1300)
                at org.springframework.beans.factory.support.ConstructorResolver. resolveAutowiredArgument(ConstructorResolver.java:887)
                at org.springframework.beans.factory.support.ConstructorResolver. createArgumentArray(ConstructorResolver.java:791)
                ... 71 more

 

 

그러니까, 트위터 연결용 토큰을 별도로 생성한 properties 파일에 넣어뒀는데,

이건 git에 올려두지 않고 vi로 직접 aws에 생성 시키는 형태라 (경로도 프로젝트 내부가 아님)

gradle의 빌드 시에는 참조하지 않는 상태가 된거죠...

자동화 쉘에서도 빌드 후 프로퍼티를 jar에 포함시키는 형태로 움직이고 있어서 에러가 발생하는건 여전했구요.

 

그리고 사실 문제는 이거뿐만이 아니었습니다.

/**
 * post api 요청을 수신하는 컨트롤러
 */
@RequiredArgsConstructor
@RestController
public class PostApiController {

    private final PostApiService postApiService;

    // 트위터 계정 & 키워드 등록용
    @PostMapping("api/account/register")
    public Long menuInfoRegister(@RequestBody InfoRegisterDto requestDto) {
        return postApiService.infoRegisterSave(requestDto);
    }
}


/**
 * post 처리를 담당하는 서비스
 */
@RequiredArgsConstructor
@Service
public class PostApiService {

    private final TwitterService twitterLoadingService;
    private final BroadcastInfoRepository broadcastInfoRepository;
    private final TweetInfoRegisterRepository infoRegisterRepository;

    // 추출 정보 등록
    @Transactional
    public Long infoRegisterSave(InfoRegisterDto requestDto) {
        List<TweetInfoRegister> infoRegisterDtoList =
                infoRegisterRepository.findAll();
        for(TweetInfoRegister dto : infoRegisterDtoList) {
            // 이미 등록 된 계정 정보라면 해당 정보를 삭제함.
            if(dto.getTwitterAccount().equals(requestDto.getTwitterAccount())) {
                infoRegisterRepository.delete(dto);
                if(requestDto.getSearchKeyword().isEmpty()) {
                    return 0L;
                }
            }
        }
        // 등록
        return infoRegisterRepository.save(requestDto.toEntity()).getId();
    }
    
    ....
}

테스트는 postApiController의 menuInfoRegister 메서드를 대상으로 하는데,

하위 서비스까지 한번에 테스트를 돌리는 걸로 테스트를 만들었으나..

infoRegisterSave에서 사용하지 않는 TwitterService 내부의 프로퍼티 읽기에서 문제가 터진거죠.

 

이는 스프링의 의존성 주입과 관련되어있는데,

테스트 시에 따로 목업 처리를 하지 않는다면, 

    private final TwitterService twitterLoadingService;
    private final BroadcastInfoRepository broadcastInfoRepository;
    private final TweetInfoRegisterRepository infoRegisterRepository;

이런 선언 된 클래스들은 죄다 의존성이 주입되어 사용할 수 있는 객체가 되는데...

그 과정에서 TwitterService 가 초기화 되면서, 토큰 설정을 불러오려 하는데 프로퍼티 파일이 없네? 에러!

같은 흐름이 생긴 것이었습니다.

 

정말, 너무 생각지도 못한 곳에서 터져서 당황스러웠는데... 

그 김에, Mockito를 도입하자고 결심하고 Mockito를 테스트에 넣었습니다.

 

    implementation 'junit:junit:4.13.1'
    implementation 'org.mockito:mockito-core:3.7.7'
    
    build.gradle 추가
    // menuInfoRegister 테스트 (Mockito 도입 전)
    @Test
    public void testMenuInfoRegister01() throws Exception {
        // 조건 설정
        String twitterAccount = "testAccount";
        String keyword = "testKeyword";
        InfoRegisterDto requestDto = InfoRegisterDto.builder()
                .twitterAccount(twitterAccount)
                .searchKeyword(keyword)
                .build();

        String url = "http://localhost:" + port + "/api/account/register";

        // 테스트 실행
        mvc.perform(post(url)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(new ObjectMapper().writeValueAsString(requestDto)))
                .andExpect(status().isOk());

        // 결과 비교
        List<TweetInfoRegister> all = infoRegisterRepository.findAll();
        assertThat(all.get(0).getTwitterAccount()).isEqualTo(twitterAccount);
        assertThat(all.get(0).getSearchKeyword()).isEqualTo(keyword);
    }

 

    // menuInfoRegister 테스트 (Mockito 도입 후)
    @Test
    public void testMenuInfoRegister01() throws Exception {
        // 조건 설정
        String twitterAccount = "testAccount";
        String keyword = "testKeyword";
        InfoRegisterDto requestDto = InfoRegisterDto.builder()
                .twitterAccount(twitterAccount)
                .searchKeyword(keyword)
                .build();

        String url = "http://localhost:" + port + "/api/account/register";

        // Mock 설정
        List<TweetInfoRegister> mockList = new ArrayList<>();
        TweetInfoRegister mockObject =
                TweetInfoRegister.builder().twitterAccount(twitterAccount)
                        .searchKeyword(keyword).build();
        mockList.add(mockObject);
        doReturn(mockList).when(infoRegisterRepository).findAll();
        Long returnValue = 0L;
        doNothing().when(infoRegisterRepository).delete(mockObject);
        doReturn(mockObject).when(infoRegisterRepository).save(any());

        // 테스트 실행
        mvc.perform(post(url)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(new ObjectMapper().writeValueAsString(requestDto)))
                .andExpect(status().isOk());

        // 결과 비교
        List<TweetInfoRegister> all = infoRegisterRepository.findAll();
        assertThat(all.get(0).getTwitterAccount()).isEqualTo(twitterAccount);
        assertThat(all.get(0).getSearchKeyword()).isEqualTo(keyword);
    }

 

 

그리고 AWS 업로드,

 

테스트 통과 되었습니다.

junit을 찾아보면서 본거지만, 단위 테스트는 최대한 의존성을 없애는게 맞다라고 하더군요.

Mock을 적재적소에 사용하면서, 의존성을 최대한 줄여보려고 합니다. 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함