2017-10-19 1 views
1

Projet: https://github.com/marioedgar/webpack-unit-testtest unitaire Vue.js - service simulé avec des données async

J'ai une application Vue.js je généré avec la CLI vue. Je n'ai édité le composant HelloWorld légèrement pour récupérer certaines données async de mon service de test, vous pouvez voir que ici:

<template> 
 
    <h1>{{ message }}</h1> 
 
</template> 
 

 
<script> 
 
    import service from './test.service' 
 
    export default { 
 
    name: 'HelloWorld', 
 
    created() { 
 
     service.getMessage().then(message => { 
 
     this.message = message 
 
     }) 
 
    }, 
 
    data() { 
 
     return { 
 
     message: 'A' 
 
     } 
 
    } 
 
    } 
 
</script> 
 
<style scoped> 
 
</style>

Le service de test réside dans le même répertoire et est très simple :

class Service { 
 
    getMessage() { 
 
    return new Promise((resolve, reject) => { 
 
     console.log('hello from test service') 
 
     resolve('B') 
 
    }) 
 
    } 
 
} 
 

 
const service = new Service() 
 
export default service

Ainsi, afin de m ock ce service, im en utilisant la webpack vue-chargeur pour injecter le service simulé tel que décrit dans la documentation officielle ici:

https://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html

Voici donc mon test qui est presque identique à l'exemple:

import Vue from 'vue' 
 
const HelloInjector = require('!!vue-loader?inject!../../../src/components/HelloWorld') 
 

 
const Hello = HelloInjector({ 
 
    // mock it 
 
    './test.service': { 
 
    getMessage() { 
 
     return new Promise((resolve, reject) => { 
 
     resolve('C') 
 
     }) 
 
    } 
 
    } 
 
}) 
 

 
describe('HelloWorld.vue',() => { 
 
    it('should render',() => { 
 
    const vm = new Vue({ 
 
     template: '<div><test></test></div>', 
 
     components: { 
 
     'test': Hello 
 
     } 
 
    }).$mount() 
 
    expect(vm.$el.querySelector('h1').textContent).to.equal('C') 
 
    }) 
 
})

Il y a deux questions que je fais face:

  1. Le test échoue car l'assertion est en cours d'exécution avant que la promesse simulée ne soit résolue. D'après ce que je comprends, c'est parce que le cycle de vie de vue n'a pas complètement terminé quand je fais mon affirmation. Le boniment commun d'attendre le prochain cycle serait de WRAPP mon affirmation autour de la fonction suivante tique comme ceci:

it('should render', (done) => { 
 
    const vm = new Vue({ 
 
     template: '<div><test></test></div>', 
 
     components: { 
 
     'test': Hello 
 
     } 
 
    }).$mount() 
 
    Vue.nextTick(() => { 
 
     expect(vm.$el.querySelector('h1').textContent).to.equal('C') 
 
     done() 
 
    }) 
 
    })

Ceci, cependant, ne fonctionne pas à moins que je imbriquer 3 nextTicks , ce qui me semble extrêmement hacky. Y a-t-il quelque chose qui me manque pour que ça marche? cet exemple semble très simple, mais je ne peux pas obtenir ce test pour passer sans beaucoup de nextTicks

  1. Je continue de recevoir une étrange erreur, par intermittence ... cet avertissement apparaît probablement 50% du temps et n'est pas Consistant à tout.

[vue warn] Impossible de monter le composant: modèle ou rendre la fonction est non définie

Encore une fois, cela ne se produit que parfois. Je peux exécuter le même test unitaire sans aucun changement et il me montrera ce message 50% du temps.

+0

Voici le lien vers mon repo public: https://github.com/marioedgar/webpack-unit-test –

Répondre

1

Je ne pouvais vraiment pas comprendre pourquoi parfois le composant ne pouvait pas être monté. Je ne suis même pas sûr que ce soit lié à l'injecteur mais, en tout cas, j'ai gardé le test cohérent en ne l'utilisant pas; essayer une approche différente à la place.

Le composant peut être plus testable si le service est injecté via le props au lieu d'être directement utilisé.

<template> 
    <div> 
    <h1>{{ message }}</h1> 
</div> 
</template> 

<script> 
    import service from './test.service' 

    export default { 
    name: 'HelloWorld', 
    created() { 
     this.service.getMessage().then(message => { 
     this.message = message 
     }) 
    }, 
    data() { 
     return { 
     message: 'A' 
     } 
    }, 
    props: { 
     service: { 
     default: service 
     } 
    } 
    } 
</script> 
<style scoped> 
</style> 

Cela rend l'injecteur inutile que le service peut être raillé au lieu transmis au composant à l'aide propsData dans le constructeur.

import Vue from 'vue' 

import Async from '@/components/Async' 

const service = { 
    getMessage() { 
    return new Promise((resolve, reject) => { 
     resolve('C') 
    }) 
    } 
} 

describe('Async.vue',() => { 
    let vm 
    before(() => { 
    const Constructor = Vue.extend(Async) 
    vm = new Constructor({ 
     propsData: { 
     service: service 
     } 
    }).$mount() 
    }) 
    it('should render', function() { 
    // Wrapping the tick inside a promise, bypassing PhantomJS's lack of support 
    return (new Promise(resolve => Vue.nextTick(() => resolve()))).then(() => { 
     expect(vm.$el.querySelector('h1').textContent).to.equal('C') 
    }) 
    }) 
}) 
+0

Je aime mieux que l'utilisation de l'injecteur, cela ne résout l'un des problèmes j'étais face. C'est malheureux que les tiques supplémentaires soient nécessaires. Im se demandant si peut le faire dans un avant() pour éviter tout ce code supplémentaire dans chaque test. Cela ou je peux ajouter une fonction d'assistance asynchrone simple. –

+0

Vous avez raison. En utilisant 'before 'de moka, les tests n'entrent que jusqu'à ce que le composant soit complètement monté, donc une seule coche est nécessaire. J'ai changé le code pour refléter cela. –