Skip to content

백엔드 cucumber를 이용한 인수테스트

Minsung Son edited this page Jan 13, 2022 · 1 revision

프롤로그는 인수테스트를 진행함에 있어 cucumber 를 사용합니다.

BDD 도구이며 시나리오를 읽을 수 있게 보여주는 장점이 있습니다.

.feature 파일이 시나리오를 작성하는 부분이며 java 패키지의 소스들이 이 시나리오의 구현체가 됩니다.

인수 조건에 대한 구현이 위와 같이 이어집니다.

Cucumber에 대한 기본적인 Configuration은 아래 문서를 참조 바람.

https://github.com/cucumber/cucumber-jvm/tree/main/spring

Cucumber 테스트 작성하기

공통

@Component
@Scope(scopeName = "cucumber-glue")
public class AcceptanceContext {

    public RequestSpecification request;
    public Response response;
    public String accessToken; // Deprecated 사용하지 마세요
    public Map<String, Object> storage;

    public AcceptanceContext() {
        reset();
    }

    private void reset() {
        request = null;
        response = null;
        accessToken = "";
        storage = new HashMap<>();
    }

    public void invokeHttpGet(String path, Object... pathParams) {
    }

    public void invokeHttpPost(String path, Object data) {
    }

    public void invokeHttpGetWithToken(String path, Object... pathParams) {
    }

    public void invokeHttpPutWithToken(String path, Object data) {
    }

    public void invokeHttpPostWithToken(String path) {
    }

    public void invokeHttpPostWithToken(String path, Object data) {
    }

    public void invokeHttpDeleteWithToken(String path) {
    }

    public void invokeHttpDeleteWithToken(String path, Object data) {
    }
}

모든 테스트 코드는 위 AcceptanceSteps를 상속받는다. 그리고 그 내부에는 위 객체가 주입되어 있다. 이 객체에는 테스트 코드를 작성함에 있어 필요한 유틸성 메서드와 필드가 있다.

각 메서드는 메서드명으로 유추가 가능하니 설명 생략한다.

request는 RestAssured객체의 동적 생성을 위한 객체이다. 실제로 사용할 일은 거의 없다.

Response는 메서드로 받아온 데이터가 저장된다. 이용 에는 뒤에서 보인다.

storage 필드는 RestCall의 결과로 나온 데이터를 임시적으로 저장할때 이용된다. 이용 예는 뒤에서 보인다

테스트 구현

public class LoginStepDefinitions extends AcceptanceSteps {

    @Given("{string}(이)(가) 로그인을 하고")
    public void 멤버가로그인을하고(String member) {
        HashMap<String, Object> data = new HashMap<>();
        data.put("code", GithubResponses.findByName(member).getCode());

        context.invokeHttpPost("/login/token", data);
        TokenResponse tokenResponse = context.response.as(TokenResponse.class);
        context.accessToken = tokenResponse.getAccessToken();

        context.storage.put("username", GithubResponses.findByName(member).getLogin());
    }

    @When("{string}(이)(가) 로그인을 하면")
    public void 로그인을하면(String member) {
        HashMap<String, Object> data = new HashMap<>();
        data.put("code", GithubResponses.findByName(member).getCode());

        context.invokeHttpPost("/login/token", data);
    }

    @Then("액세스 토큰을 받는다")
    public void 액세스토큰을받는다() {
        TokenResponse tokenResponse = context.response.as(TokenResponse.class);

        assertThat(tokenResponse).isNotNull();
    }
}

예시가 가장 잘 드러나는 LoginStepDefinitions 객체를 예시로 보인다.

Cucumber는 @Given, @When, @Then 과 같은 Annotation을 통해 테스트의 순서를 명시한다.

이 외에도 @And와 같은 어노테이션도 존재하니 한번 확인해 보고 필요할때마다 사용해 보도록 하자.

이렇게 작성된 로직은

@api
Feature: 로그인 기능

    Scenario: 로그인하기
    When "브라운"이 로그인을 하면
    Then 액세스 토큰을 받는다

위와같은 형식으로 사용되게 되는데 보면, 어노테이션에 {string} 으로 표시된 부분은 “브라운” 으로 정의되고 그 뒤에 (이)(가) 는 조사로서 사용되는것을 볼 수 있다.

위 구현체를 보면 invoke 메서드를 호출한 뒤에는 context.response를 통해 그 결과를 사용하는것을 볼 수 있다(사실 리팩터링 대상이다).

그리고 위에서는 accessToken을 사용하기 위해 context.accessToken을 사용하는것을 볼 수 있는데, 이는 초창기에 로그인만을 생각하고 만든 코드이기 때문이다.

확장성을 위해 context.storage를 이용하면 response로 온 여러 데이터를 저정하고 다음 step에서 활용할 수 있다.

위 소스를 보면 storage에 username을 저장하고 이를 다른 로직에서 활용할 수 있게 하는것을 볼 수 있다.

이정도면 코드 작성 가능하지 않을까??

Clone this wiki locally