Skip to content

Removing sync mode #1137

Closed
Closed
@eddyerburgh

Description

@eddyerburgh

After searching for an alternative solution, we've decided to remove synchronous updating (sync mode) from Vue Test Utils.

tl;dr

We will remove sync mode in the next beta version. Test code will change from this:

it('render text', (done) => {
    const wrapper = mount(TestComponent)
    wrapper.trigger('click')
    wrapper.text().toContain('some text')
})

To this:

it('render text', async () => {
    const wrapper = mount(TestComponent)
    wrapper.trigger('click')
    await Vue.nextTick()
    wrapper.text().toContain('some text')
})

Background

By default, Vue batches updates to run asynchronously (on the next "tick"). This is to prevent unnecessary DOM re-renders, and watcher computations (see the docs for more details).

When we started Vue Test Utils, we decided to make updates run synchronously. The reasoning was that synchronous tests are easier to write, and have improved error handling.

For context, the decision was made just after async/await had been released in node 7.6, so asynchronous tests often looked like this:

it('render text', (done) => {
    const wrapper = mount(TestComponent)
    wrapper.trigger('click')
    Vue.nextTick(() => {
        wrapper.text().toContain('some text')
        wrapper.trigger('click')
        Vue.nextTick(() => {
            wrapper.text().toContain('some different text')
            done()
        })
    })
})

Because of this decision Vue Test Utils runs updates synchronously by default. That was a mistake.

Why?

Sync mode causes bugs that don't exist when Vue runs normally. This is frustrating for users, and bad functionality for a testing framework that's intended to give you confidence that your code will work in production.

We went through three different approaches to implement sync mode. Each had problems. The final attempt was to reimplement synchronous updates in Vue core, but there were still bugs caused by synchronous updates.

Solution

The solution is to remove sync mode entirely from Vue Test Utils and rely on the user explicitly waiting for updates to be applied:

it('render text', async () => {
    const wrapper = mount(TestComponent)
    
    wrapper.trigger('click')
    await Vue.nextTick()
    
    wrapper.text().toContain('some text')
    
    wrapper.trigger('click')
    await Vue.nextTick()
    wrapper.text().toContain('some different text')
})

This will be a big change for many test suites. You have two choices:

  1. Refactor tests to run asynchrnously
  2. Keep Vue Test Utils locked at beta.28

We recommend that you update your tests in order to benefit from future features of Vue Test Utils. This will also make your tests more robust since Vue will perform updates in the same way as it does in production.

To make the migration as smooth as possible, so we'll provide documentation and guides to help you write tests asynchronously with Vue.nextTick.

Finally, I'm sorry for the work this change will require. I was a large part of the driving force for running in sync mode by default, and I underestimated the problems that it would cause.

Going forward, this will improve the stability of Vue Test Utils, and we'll be able to release a stable API as v1.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions