본문 바로가기
Computer Engineering

github action matrix strategy 활용

by ybs 2022. 11. 13.
반응형

상황 설명

1. 우리 프로젝트는 PR 올리면 자동으로 테스트를 실행시키기 위해(CI 빌더로써) github action 을 활용한다.

2. 네이버망에 있는 서버와 API 통신을 할 때 Proxy 서버를 거쳐서 가도록 수정이 필요하다(보안 요구사항).

 

WebClient 는 아래 코드와 같이 isSecurityProxyEnabled 가 true 인 경우(Proxy 서버를 거쳐가야 하는 경우), proxy 메서드를 통해 Proxy 서버 주소와 포트를 알려준다.

	@Value("${security-proxy.enabled:false}")
	private boolean isSecurityProxyEnabled; // true 이면, proxy서버를 거쳐야 한다


	HttpClient httpClient = HttpClient.create(connectionProvider);

	return isSecurityProxyEnabled
		? httpClient
		.proxy(it ->
			it.type(ProxyProvider.Proxy.HTTP)
				.host(proxy서버 도메인)
				.port(443))
		: httpClient;

 

문제 상황

deploay phase 에 따라 isSecurityProxyEnabled 값이 달라져야 한다. 먼저 로컬에서 실행시킬때와 로컬에서 테스트를 돌릴 때는 Proxy 를 거치지 않도록 설정한다. 다시말해 deploy phase local 과 test 는 false 여야 한다.

 

하지만 PR 올리면 trigger 로 동작하는 CI 빌더에서는 deploy phase test 도 true 로 동작해야한다. 그래서 github action workflows yml 파일에 true 를 주입하게 해서 해결했다.

      - name: test
        run: |
          SECURITYPROXY_ENABLED=true \

 

그런데 또다른 문제가 생겼다. 테스트코드 중에서도 wire mock 으로 진행하는 internal 테스트와 실제 api 를 호출하는 external 테스트가 있는데, internal 테스트에서 에러가 나는것이다. 그래서 internal 일때와 external 일때를 구분지어 CI 빌더가 동작할 필요가 있고 github action matrix strategy 를 활용했다.

 

해결 방법

아래와 같이 github action yml 을 설정하면 step 이 external 일때 한번, internal 일때 한번 총 두번 병렬로 동작한다. 

jobs:
  test-api:
    strategy:
      matrix:
        module: [ 'external', 'internal' ]
    steps:
      - name: api external test
        if: ${{ matrix.module }} == 'external'
        run: |
          SECURITYPROXY_ENABLED=true \
          -Dtest=external
          ... 생략
      - name: api internal test
        if: ${{ matrix.module }} == 'internal'
        run: |
          SECURITYPROXY_ENABLED=false \
          -Dtest=internal
          ... 생략

 

추가 설명

위 github action yml 설정에 -Dtest=external 과 -Dtest=internal 도 있다. java 옵션 명령어 인 -D<name>=<value> 는 system property 를 추가한다. 그리고 각 모듈 build.gradle 에서 "test" 시스템 변수를 가져와서 external 이면 includeTags 에 external 을 넣고  failFast 를 false 로 한다. 

test {
    useJUnitPlatform {
        def testScope = System.getProperty("test")
        if (testScope == "external") {
            includeTags "external"
            failFast false
        } else if (testScope == "internal") {
            excludeTags "external"
            failFast true
        } else {
            failFast true
        }
        ...
}

 

includeTags 에 external 을 넣음으로써 아래 @ExternalTest 애노테이션이 붙은 테스트들이 동작하게 된다.

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@ActiveProfiles("external")
@Tag("external")
annotation class ExternalTest

 

failFast 는 첫 번째 테스트에 실패한 후 테스트 실행을 중지할지 여부이다. external test 는 연동된 수많은 서버들을 mocking 없이 실제로 호출한다. 문제는 연동된 서버가 개발 서버이기 때문에 쉽게 죽거나 순단이 발생할 수 있다(개발서버 배포로 인해). 워낙 자주 깨지는 테스트다 보니 false 로 설정했다.

package org.gradle.api.tasks.testing;

public class Test extends AbstractTestTask implements JavaForkOptions, PatternFilterable {
    @Option(
        option = "fail-fast",
        description = "Stops test execution after the first failed test."
    )
    public void setFailFast(boolean failFast) {
        super.setFailFast(failFast);
    }
반응형