Front-end testing

Original link: https://ssshooter.com/front-end-test

Vitest

Vitest is a testing framework, similar to the old framework Jest, for running tests. The biggest advantage of Vitest is that it can be integrated with Vite to reduce configuration complexity. If you don’t use Vite, Vitest may not be the best choice.

As a test framework, the most critical elements are describe , test ( it ) and expect :

 import { describe , expect , test } from 'vitest' const person = { isActive : true , age : 32 , } describe ( 'person' , ( ) => { test ( 'person is defined' , ( ) => { expect ( person ) . toBeDefined ( ) } ) test ( 'is active' , ( ) => { expect ( person . isActive ) . toBeTruthy ( ) } ) test ( 'age limit' , ( ) => { expect ( person . age ) . toBeLessThanOrEqual ( 32 ) } ) } )

The more troublesome thing is mocking .

Mock is to create some test data in the test. When calling functions, especially complex useX , mocks are especially needed.

Here are some common MockInstance Method

vi. spyOn

spyOn can be used to monitor a function, count the number of calls, or use methods such as mockReturnValue to fake functions.

Here is an official example:

 // some-path.js export const getter = 'variable' // some-path.test.ts import * as exports from './some-path.js' vi . spyOn ( exports , 'getter' , 'get' ) . mockReturnValue ( 'mocked' )

It can be seen that spyOn can monitor the entire module and intervene in the behavior of a certain output value in the module, including variables and functions. But in fact, there are some problems. Self-defined modules are ok, but some modules cannot spyOn correctly, such as vue-router:

 import { useRoute } from 'vue-router' vi . spyOn ( exports , 'useRoute' ) . mockReturnValue ( { path : '/path' } as any )

The test failed to run and returned TypeError: Cannot redefine property: useRoute for an unknown reason.

vi.fn

vi.fn() and vi.spyOn() share the same method, but only the result of vi.fn() is callable.

According to the description on the official website, vi.fn() returns an executable function, while vi.spyOn() only modifies the target behavior. In practice, I still feel that vi.spyOn() is more practical for the time being. Generally, it only modifies the target behavior and allows the page to simulate the call. It does not need to call the simulated function on the test file.

vi.mock

Replaces all imported modules with another module using the provided path. The call to vi.mock is elevated, so it doesn’t matter where you call it. It will always be executed before all imports.

Because vi.mock will be promoted, there is no need to write vi.mock in beforeEach , and only one will take effect

 // ./some-path.js export function method ( ) { } // some-path.test.ts import { method } from './some-path.js' vi . mock ( './some-path.js' , ( ) => ( { method : vi . fn ( ) , } ) )

So the above routing problem can be solved like this:

 import { useRoute } from 'vue-router' vi . mock ( 'vue-router' , async ( importOriginal ) => { const vr = await importOriginal ( ) return { ... ( vr as any ) , useRoute : vi . fn ( ) } } ) // mock 方法时vi . mocked ( useRoute ) . mockReturnValue ( { path : '/system-pipeline-template' } as any )

In fact, the test file is really counter-intuitive, and it is difficult to reflect what useRoute uses from the code. Although you see import { useRoute } from 'vue-router' , but because of the use of vi.mock , the final useRoute is actually vi.fn() . vi.mocked is actually a Typescript helper function that can help you pass type detection. If you use JavaScript, you can ignore it.

Environment variables and global variables

 vi . stubGlobal ( '__VERSION__' , '1.0.0' ) vi . stubEnv ( 'VITE_ENV' , 'staging' )

Compared with function testing, user interface component testing is a big front-end problem. The testing framework Vitest itself cannot solve this problem. We also need to use Vue Test Utils.

beforeEach

When to use beforeEach?

Use methods such as vi.clearAllMocks to clear mocks. When each test case needs to use the same mock, it can be processed here.

__mocks__

Vue Test Utils

Vue Test Utils , referred to as VTU, is used to simulate mounting components and triggering events.

 import { mount } from '@vue/test-utils' import TodoApp from './TodoApp.vue' test ( 'creates a todo' , ( ) => { const wrapper = mount ( TodoApp ) expect ( wrapper . findAll ( '[data-test="todo"]' ) ) . toHaveLength ( 1 ) wrapper . get ( '[data-test="new-todo"]' ) . setValue ( 'New todo' ) wrapper . get ( '[data-test="form"]' ) . trigger ( 'submit' ) expect ( wrapper . findAll ( '[data-test="todo"]' ) ) . toHaveLength ( 2 ) } )

The above code is to use mount function to simulate mounting TodoApp component in the test, and simulate the operation of the element in the obtained result wrapper to achieve the test effect.

The returned wrapper has these methods . You can use methods such as setData to modify component values, and you can also access ComponentPublicInstance through vm properties. These methods and properties provide methods for testing the internal operation details of components, but this is only a genre of testing, which will be mentioned later Vue Testing Library recommends that we try to be as close to the actual operation as possible.

Vue Testing Library

Vue Testing Library is a layer of encapsulation based on VTU.

Testing Library encourages you to avoid testing implementation details like the internals of a component you’re testing (though it’s still possible). The Guiding Principles of this library emphasize a focus on tests that closely resemble how your web pages are interacted by the users .

Testing Library does not advocate the implementation details of test components. It will deliberately hide information such as instance , making it difficult for you to test the methods on the instance. You need to check whether the elements are displayed correctly after interacting with the page to judge whether the test passes.

Therefore, whether to use VTL or not depends on your habit of writing tests. However, the existence of VTL certainly proves that testing that ignores details is reasonable. This article , Testing Implementation Details, explains to you why you should not test implementation details. You can find out.

Because the details are hidden, the testing method of VTL is relatively simpler. This page cheatsheet basically tells you all the testing methods.

mock input props

https://github.com/testing-library/vue-testing-library/blob/main/src/__tests__/vue-router.js

If you use a UI library, it may be difficult to trigger components. You can try to use container.querySelector to find elements for precise operations.

practice

Create a new Vite project and install Vitest directly:

 pnpm add -D vitest

and add two commands:

 { "scripts" : { "test" : "vitest" , "coverage" : "vitest run --coverage" } }

Vitest reads the configuration in vite.config.ts , and runs npm run test directly to perform the simplest test. Try running:

 include: **/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx} exclude: **/node_modules/**, **/dist/**, **/cypress/**, **/.{idea,git,cache,output,temp}/**, **/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.* watch exclude: **/node_modules/**, **/dist/** No test files found, exiting with code 1

Although it has no effect, you can know the scope of the test file from it.

Finally, I still have to say that even if the detailed test is neglected, it is easy to test for leaks, and make up for leaks. Over time, there will be a lot of tests. If the construction period is not very sufficient, please do not write tests.

This article is transferred from: https://ssshooter.com/front-end-test
This site is only for collection, and the copyright belongs to the original author.