5 - Vue component 테스트

업데이트: Link

5장 Vue component 테스트

Written by Alex Jover Morales

시작하기 위해 name 로컬 상태 변수를 기반으로 일부 텍스트를 인쇄하는 매우 간단한 Hello.vue 구성 요소를 만들어 보겠습니다.

<template>
  <div>Hello !</div>
</template>

<script>
export default {
  data() {
    return { name: "Vue" };
  },
};
</script>

Vue 구성 요소는 내부 또는 외부의 일부 상태를 기반으로 DOM의 일부를 렌더링하는 원자적 조각입니다. 이러한 이유로 스냅샷 테스트를 사용하는 것이 완벽한 대상이지만 나중에 다시 설명하겠습니다.

지금은 Hello 구성 요소를 가져오고 아직 더 많은 작업을 수행하지 않는 테스트를 설정해 보겠습니다. 우리는 테스트 주장을 우회하기 위해 expect(true).toBe(true)를 사용할 수 있습니다:

// hello.test.js
import Hello from "./Hello";

describe("Hello component", () => {
  it("renders correctly", () => {
    expect(true).toBe(true);
  });
});

이 테스트를 실행하려고 하면 다음 오류가 발생합니다.

Cannot find module ‘./Hello’ from ‘hello.test.js’

.vue 확장자가 없기 때문이라고 생각할 수 있습니다. 글쎄요, 당신은 그것을 변경하려고 할 수 있습니다:

import Hello from "./Hello.vue";

하지만 이제 또 다른 오류가 발생합니다.

> SyntaxError: Unexpected token {
>     it("renders correctly", () => {
>         const comp = new Vue(Hello).$mount();
>         expect(comp.$el).toMatchSnapshot();
>     });
> });

보시다시피 인스턴스 속성 $el에 액세스하여 HTML 출력에 액세스할 수 있습니다. 동일한 테스트 수준에서 스냅샷 폴더 아래에 생성된 스냅샷을 검사하면 다음 스냅샷이 생성되는 것을 볼 수 있습니다.

exports[`Hello component renders correctly 1`] = `"Hello Vue!"`;

State based tests

다른 테스트를 추가해 보겠습니다. 이제 로컬 상태에서 name 변수를 변경할 때 예상대로 업데이트되는지 확인할 것입니다.

따라서 다음 테스트의 경우:

it("renders correctly after changing the name state", () => {
    const comp = new Vue(Hello).$mount();
    comp.name = "Camel";
    expect(comp.$el).toMatchSnapshot();
})

Hello Camel과 함께 스냅샷을 출력해야 합니다! 하지만 우리가 그것을 조사한다면 우리는 그것이 틀렸다는 것을 깨닫게 될 것입니다:

exports[`Hello component renders correctly after changing the name state 1`] = `
<div>
  Hello Vue!
</div>
`;

왜 업데이트되지 않았습니까? Vue는 DOM 업데이트를 비동기식으로 수행하므로 name 상태를 업데이트하고 이를 주장할 때 아직 DOM에 전파되지 않았습니다.

이 문제를 방지하려면 $nextTick 인스턴스 메서드를 사용하여 다음 업데이트 주기를 기다려야 합니다.

it("renders correctly after changing the name state", () => {
  const comp = new Vue(Hello).$mount();
  comp.name = "Camel";

  comp.$nextTick(() => {
    expect(comp.$el).toMatchSnapshot();
  });
});

시도하고 통과했기 때문에 예상대로 작동하는 것처럼 보이더라도 사실이 아닙니다. 실제로 일어나고 있는 것은 나중에 비동기적으로 호출되기 때문에 예상 함수를 실행하지 않고 테스트가 완료된다는 것입니다.

비동기 호출을 위해 Jest에서 async/await를 사용하는 방법을 보았지만 $nextTick은 콜백 기반이므로 이 경우에는 유용하지 않습니다.

콜백 기반 비동기 테스트의 경우 Jest는 우리가 원할 때마다 호출할 수 있는 it 함수에 콜백 인수를 정의할 수 있으며 Jest는 호출될 때까지 대기합니다. 따라서 cb 인수를 제공하고 expect 검사 후에 호출하여 테스트를 업데이트하여 안전하도록 할 수 있습니다.

it("renders correctly after changing the name state", done => {
  const comp = new Vue(Hello).$mount();
  comp.name = "Camel";

  comp.$nextTick(() => {
    expect(comp.$el).toMatchSnapshot();
    done();
  });
});

done 함수가 테스트의 특정 지점에서 실제로 호출되었는지 확인하십시오. 그렇지 않으면 시간 초과 오류가 발생합니다.

저는 done이라고 이름을 지었지만 원하는 대로 이름을 지정할 수 있습니다.

Props based tests

state와 마찬가지로 props를 기반으로 다른 render state를 가질 수 있습니다.

props 값을 컴포넌트의 props에 전달하려면 propsData 컴포넌트 옵션을 사용해야 합니다. 그러나 현재 구성 요소를 정의하는 방식에서는 propsData를 정의할 방법이 없습니다. 구성 요소를 Vue에 인수로 전달하기 때문입니다.

it("renders correctly with different props", () => {
  const comp = new Vue(Hello).$mount();
  expect(comp.$el).toMatchSnapshot();
});

대신에 우리가 할 수 있는 일은 propsData와 같은 매개변수를 추가할 수 있도록 구성 요소를 확장하는 것입니다. 구성 요소를 확장하기 위해 Vue.extend 메서드를 사용할 수 있습니다.

it("renders correctly setting the `greeter` prop", () => {
  const props = { greeter: "Joe" };
  const Constructor = Vue.extend(Hello);
  const comp = new Constructor({ propsData: props }).$mount();

  expect(comp.$el).toMatchSnapshot();
});

구성 요소를 확장할 때 자체 Constructor를 만듭니다. 그런 식으로 새 Constructor({ propsData: props }) 부분에서 원하는 구성 요소 옵션을 전달하는 새 인스턴스를 만들 수 있습니다.

테스트를 실행할 때 구성 요소 템플릿의 코드를 변경했기 때문에 u를 눌러 스냅샷을 업데이트해야 합니다.

마지막 스냅샷에는 Joe가 인사하는 사람과 함께 다음 메시지가 표시되어야 합니다.

exports[`Hello component renders correctly setting the \`greeter\` prop 1`] = `
<div>
  Hello Vue from Joe!
</div>
`;

Bonus: 보너스: vue-test-utils 사용

Vue 핵심 팀의 Edd Yerburgh 는 Vue 구성 요소를 쉽게 테스트할 수 있도록 하는 일련의 유틸리티가 포함된 패키지인 vue-test-utils를 개발했습니다. 프레임워크에 구애받지 않는 테스트이므로 mocha 또는 선택한 다른 도구에서 사용할 수 있습니다.

테스트에서 대부분의 일반적인 작업에 대한 도우미 메서드가 있을 뿐만 아니라 하위 구성 요소 렌더링을 방지하는 기술인 얕은 렌더링을 쉽게 적용할 수 있습니다. 이 개념을 자세히 설명하는 것은 이 시리즈의 범위를 벗어납니다. 그러나 얼마 전에 작성한 이 기사 에서 더 많은 것을 읽을 수 있습니다.

이 특별한 경우에는 몇 가지 간단한 테스트만 있지만 vue-test-utils를 사용하여 테스트를 하나씩 작성하면 어떻게 도움이 되는지 알아보겠습니다.

설치부터 시작하겠습니다.

npm install --save-dev @vue/test-utils

그럼 첫 번째 테스트를 다시 작성해 보겠습니다. 깊은 렌더링 또는 얕은 렌더링을 원하는지 여부에 따라 mount 또는 shallow 중 하나를 가져와야 하며 둘 다 추가 메서드 세트와 함께 구성 요소의 래퍼를 반환합니다. 이 경우 Hello 구성 요소에 자식이 없으므로 차이가 없으므로 mount를 사용할 것입니다.

import Vue from "vue";
import { mount } from "@vue/test-utils";
import Hello from "./Hello.vue";

describe("Hello component", () => {
    it("renders correctly", () => {
        const comp = mount(Hello);
        expect(comp.html()).toMatchSnapshot();
    });
    //...
})

구성 요소를 mount에 전달하기만 하면 래퍼가 생성됩니다. $el 속성에 액세스하는 대신 vue-test-utils는 HTML 렌더링된 출력을 얻기 위해 html()과 같은 추상화를 제공합니다.

Vue를 더 이상 사용하지 않으므로 제거할 수 있습니다. 다른 테스트가 다시 작성될 때까지 실패하지 않도록 끝까지 유지하고 있습니다.

테스트를 실행하면 스냅샷이 다르기 때문에 실패하는 것을 알 수 있습니다. 그러나 자세히 살펴보면 출력은 정확하지만 html() 메서드가 일부 형식 지정을 수행한다는 것을 알 수 있습니다. 이는 훌륭합니다.

이를 알고 있으면 여전히 watch 모드에서 테스트를 실행 중인 경우 u 키를 눌러 스냅샷을 안전하게 업데이트할 수 있습니다.

다음 테스트로 넘어가겠습니다. 다음 DOM 업데이트가 적용될 때까지 기다리려면 $nextTick을 사용해야 했습니다.

it("renders correctly after changing the name state", () => {
  const comp = new Vue(Hello).$mount();
  comp.name = "Camel";

  comp.$nextTick(() => {
    expect(comp.$el).toMatchSnapshot();
  });
});

이 경우 vue-test-utils는 내부적으로 DOM 업데이트를 강제하는 로컬 상태 업데이트를 위한 setData() 메서드를 제공하므로 자체적으로 처리할 필요가 없어 테스트가 훨씬 간단해집니다.

it("renders correctly after changing the name state", () => {
  const comp = mount(Hello);
  comp.setData({ name: "Camel" });
  expect(comp.html()).toMatchSnapshot();
});

마지막 테스트를 살펴보겠습니다.

it("renders correctly setting the `greeter` prop", () => {
  const props = { greeter: "Joe" };
  const Constructor = Vue.extend(Hello);
  const comp = new Constructor({ propsData: props }).$mount();

  expect(comp.$el).toMatchSnapshot();
});

propsData 옵션과 같은 추가 매개변수를 전달하기 위해 mountshallow 함수의 두 번째 인수를 활용할 수 있습니다.

it("renders correctly setting the `greeter` prop", () => {
  const comp = mount(Hello, {
    propsData: {
      greeter: "Joe",
    },
  });

  expect(comp.html()).toMatchSnapshot();
});

이런 식으로 추가 구성 요소 옵션을 추가하기 위해 구성 요소를 확장할 필요가 없으며 바로 수행할 수 있습니다.

이제 모든 테스트를 다시 작성했으므로 더 이상 필요하지 않으므로 Vue를 가져오는 행을 제거할 수 있습니다.

import Vue from "vue";

vue-test-utils에는 소품, 트리거 이벤트, 모의 슬롯 등을 조작하기 위한 도우미 메서드가 있습니다. Vue에는 실제 애플리케이션 테스트 스위트를 작성하는 데 사용할 수 있는 자체 공식 테스트 유틸리티가 있다는 것을 알기 위해 여기에서 아주 간단한 소개를 보았습니다.

마무리

이 시리즈에서 우리는 처음부터 테스트가 무엇인지 알기 위해 시작하여 테스트에 대한 개념을 점진적으로 추가하고 JavaScript에 적용하는 방법과 Vue 구성 요소 테스트의 표면을 긁는 것으로 마무리하는 등 테스트 사례를 자세히 살펴보았습니다.

테스트를 처음 접하는 경우 이 시리즈를 전체적으로 따를 수 있고 이미 경험이 있는 경우 이 시리즈에서 새로운 것을 배울 수 있기를 바랍니다.

댓글남기기