아래의 컴포넌트는 vuex의 actions를 사용하는 코드이다. Vuex를 사용하는 컴포넌트를 테스트시에 주의해야 할 점은 actions가 어떻게 동작하는지는 중요하지 않다. 우리가 사용하고자 하는 action이 실제로 호출되었는지 여부만 테스트 한다. action이 정확하게 동작하는지 여부는 vuex store 테스트시에 하게 된다.
it('dispatches "actionInput" when input event value is "input"', () => { const input = wrapper.find('input'); input.element.value = 'input'; input.trigger('input'); expect(actions.actionInput).toHaveBeenCalled(); });
it('does not dispatch "actionInput" when event value is not "input"', () => { const input = wrapper.find('input'); input.element.value = 'no input'; input.trigger('input'); expect(actions.actionInput).not.toHaveBeenCalled(); });
it('calls store action "actionClick" when button is clicked', () => { const button = wrapper.find('button'); button.trigger('click'); expect(actions.actionClick).toHaveBeenCalled(); }) });
첫번째 주의해야 할 점은 테스트 시에는 localVue를 사용해야 한다는 것이다. 두번째 는 actions를 jest의 mock functions를 사용하여 테스트한다. 이를 이용하면 actions를 실제로 구현하지 않아도 호출되었는지 여부를 확인 할수 있다. 세번째 는 함수의 호출 여부를 확인 하는 assertion은 toHaveBeenCalled를 사용한다.
getters를 mock 하기
getters도 actions와 마찬가지로 어떻게 동작하는지는 중요하지 않다. getters의 결과가 실제로 렌더링 되는지 여부만 확인 해보면 된다.
describe('Getters.vue', () => { let getters:any; let store:any; let wrapper:Wrapper<Vue>;
beforeEach(() => { getters = { clicks: () =>2, inputValue: () =>'input' }; store = new Vuex.Store({ getters }); wrapper = shallowMount(VuexComponent, { store, localVue }); }); it('Renders "store.getters.inputValue" in first p tag', () => { expect(wrapper.find('.input-value').text()).toBe(getters.inputValue()); });
it('Renders "store.getters.clicks" in second p tag', () => { expect(wrapper.findAll('p').at(1).text()).toBe(getters.clicks().toString()); }); });
첫번째 는 actions와는 달리 getters는 jest의 mock functions를 사용하지 않는다. getters 객체를 작성하지만 로직은 중요하지 않는다. 두번째 는 getters의 리턴 값이 렌더링되었는지 여부를 확인 하는 건 wrapper의 text 메소드를 이용한다.
it('calls store action "moduleActionClick" when button is clicked', () => { wrapper.find('button').trigger('click'); expect(actions.moduleActionClick).toHaveBeenCalled(); })
it('renders "state.clicks" in first p tag', () => { expect(wrapper.find('p').text()).toBe(state.clicks.toString()); }); });
Vuex Store 테스트하기
지금까지는 Vuex를 사용하는 컴포넌트에 대해서 알아보았다. 그렇기 때문에 실제 Vuex의 내부 동작과는 무관한 테스트였다. 이번에는 Vuex가 정확하게 동작하는지 여부를 테스트 하기 위한 방법이다. Vuex Store 테스트는 두가지 방법이 있다. 첫번째는 getters, mutations, actions를 독립적으로 테스트 하는 것이다. 두번째는 실제 Vuex Store를 생성하는 방법이다. 아래는 테스트에서 사용할 mutations와 getters 코드이다.
독립적으로 테스트 하는 경우에는 좀더 상세하게 테스트를 진행할수 있다. 테스트가 실패하더라도 어디에서 실패했는지 찾기가 쉽다. 단점으로는 commit, dispatch같은 Vuex의 함수들을 mock 해야할 필요가 있다. 이는 유닛 테스트는 성공할지라도 mock이 정확하지 않기 때문에 실제 production 코드는 실패할수도 있다.
1 2 3 4 5 6 7
import mutations from'../mutations';
test('"increment" increments "state.count" by 1', () => { const state = { count: 1 }; mutations.increment(state); expect(state.count).toBe(2); });
1 2 3 4 5 6 7 8 9 10 11
import getters from'../getters';
test('"evenOrOdd" returns even if "state.count" is even', () => { const state = { count: 2 }; expect(getters.evenOrOdd(state)).toBe('even'); });
test('"evenOrOdd" returns odd if "state.count" is odd', () => { const state = { count: 1 }; expect(getters.evenOrOdd(state)).toBe('odd'); });
store를 사용한 테스트
실제로 Vuex sotre를 사용한 방법이다. 이 테스트의 장점은 Vuex function들을 mock 할 필요가 없다는 것이다. 하지만 테스트 실패시에 어디에서 문제가 발생했는지 찾기가 어렵다.
const localVue = createLocalVue(); let store:any; localVue.use(Vuex);
beforeEach(() => { store = new Vuex.Store(cloneDeep(storeConfig)); });
test('increments "count" value when "increment" is committed', () => { expect(store.state.count).toBe(0); store.commit('increment'); expect(store.state.count).toBe(1); })
test('updates "evenOrOdd" getter when "increment" is committed', () => { expect(store.getters.evenOrOdd).toBe('even'); store.commit('increment'); expect(store.getters.evenOrOdd).toBe('odd'); })
해당 테스트에서는 cloneDeep를 사용하고 있는데 이는 각 테스트에 store를 클린하게 사용하기 위해서 이다.
그리고 생각해 보았을 때 상황에 맞추어서 하나씩만 적용 하던가 아니면 두개 모두를 적용해서 테스트 코드를 작성 하면 좋을거 같다.
vue 라우터를 테스트 할 시에 global Vue에 직접 추가를 해서는 안된다. vue 라우터를 설치 할 경우에는 $route와 $router가 vue 프로퍼티에 추가되어진다. 이는 $route와 $router를 mock 해서 테스트 하는 경우에 테스트를 실패하게 만든다. vue-test-utils에서 제공하는 createLocalVue을 사용하는 것을 권장한다.
TypeScript diagnostics (customize using `[jest-config].globals.ts-jest.diagnostics` option): src/components/__tests__/HelloWorld.test.ts:12:23 - error TS2339: Property 'message' does not exist on type 'CombinedVueInstance<Vue, object, object, object, Record<never, any>>'.
그래서 정상적으로 테스트 하기 위한 방법은 두가지 정도 인거 같다.
1. vm.$data 사용하기
vm에는 data값을 가지고 있는 $data 객체가 존재한다. 해당 객체를 통해서 검증을 하면 가능하다.
1
expect(wrapper.vm.$data.message).toBe('message');
2. jest.config.js 수정하기
ts-jest를 사용하면 diagnostics옵션의 디폴트값이 true인데 위 코드 처럼 사용할 경우 에러를 발생시킨다. 이를 false로 설정하면 정상적으로 테스트가 가능하다.
테스트 코드를 작성하다 보면 테스트마다 공통적으로 사용이 되는 코드가 존재한다. 그중에서 테스트 코드 로직을 실행 하기 이전에 실행이 되어야 하는 공통 코드가 있다. 이때는 사용되는 함수가 beforeEach 함수이다. 해당하는 함수는 각 테스트 코드가 실행되기 이전에 호출된다.
1 2 3
beforeEach(() => { // 보통 변수의 초기화 등의 코드가 들어가거나 테스트 코드 전에 실행되어야 할 로직이 들어간다. });
테스트 종료시 호출
각 테스트가 종료 후에 호출이 되는 함수도 존재한다. 이는 afterEach 함수이다.
1 2 3
afterEach(() => { // 테스트 종류 후에 작업 해야 할 로직이 들어간다. })
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript. By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules". Here's what you can do: • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns"in your config. • If you need a custom transformation specify a "transform" option in your config. • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs: https://jestjs.io/docs/en/configuration.html Details: D:\GIT 소스코드 저장소\TDD\JEST\vue-jest\vue-jest-start\src\test\helloworld.test.js:1 ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import { mount } from '@vue/test-utils';
1. 해당 라이브러리가 정상적으로 설치 되어 있는지 확인한다.
1) jest 2) vue-jest 3) @babel/core 4) @babel/preset-env 5) babel-jest 6) @vue/test-utils 7) babel-core@^7.0.0-bridge.0 (바벨이 7.0 이상 사용중일때)
package.json을 아래와 같이 수정한후에 npm run test라고 실행하면 테스트를 시작한다.
1 2 3 4 5
{ "scripts" : { "test": "jest" } }
describe와 it을 이용한 테스트 코드 작성
위에서는 test 함수를 통해서 테스트 코드를 작성하는 방법에 대해서 알아보았다. Jest에서는 이 뿐 아니라 describe와 it을 통해서도 작성 가능하다. describe은 일종의 테스트 그룹이고, it은 작은 단위의 테스트이다. it이 위의 test와 같은 단위이다.
1 2 3 4 5
describe('', () => { it('', () => { // 테스트 코드 작성 }) });