# 五.补充功能(页面测试)

前言

前端测试需要用到工具,以及测试相关工具用法,测试相关场景

监听测试用例运行,每次修改文件会自动重新跑测试用例,不用每次手动跑

yarn run test:unit --watch
1

# 1.unit

测试 props 传值

  • Test1.vue
<template>
  <h1>{{ msg }}</h1>
</template>
1
2
3
{
  props: {
    msg: String;
  }
}
1
2
3
4
5

test1.spec.js

const msg = "new message";
const wrapper = shallowMount(Test1, {
  propsData: { msg },
});
expect(wrapper.text()).to.include(msg);
1
2
3
4
5

测试点击事件

Test2.vue

<template>
  <span id="count">{{ count }}</span>
  <button @click="increment">点击</button>
  <template></template>
</template>
1
2
3
4
5
{
  data() {
  return { count: 10};
},
methods: {
  increment() {
    this.count++;
  }
}
}
1
2
3
4
5
6
7
8
9
10

test2.spec.js

let wrapper = shallowMount(Test2);
wrapper.setData({ count: 10 });
expect(wrapper.find("span").text()).to.be.equal("10");
wrapper.find("button").trigger("click");
expect(wrapper.find("span").text()).to.be.equal("11");
1
2
3
4
5

测试自定义方法

Test3.vue

<template>
  <Child @show="show"></Child>
  <p id="content" v-if="flag">{{ name }}</p>
</template>
1
2
3
4
props:{
  fn:{}
},
methods:{
  clickMe(){
    this.fn('123')
    this.fn('456')
  }
}

1
2
3
4
5
6
7
8
9
10

test3.spec.js

let wrapper = shallowMount(Test3);
expect(wrapper.find("#content").exists()).to.be.false;
wrapper.find(Child).vm.$emit("show");
expect(wrapper.find("#content").exists()).to.be.true;
1
2
3
4

测试子组件触发父组件方法

Child.vue

<template>
<button @click="clickMe">click</button>
</tempalte>
1
2
3
import Child from "./Child.vue";
export default {
  data() {
    return {name: "javascript", flag: false };
  },
  methods: {
    show() {
      this.flag = true;
    }
  },
   components: {
      Child
    }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

child.spec.js

let callback = sinon.spy();
let wrapper = shallowMount(Child, {
  propsData: { fn: callback },
});
wrapper.find("button").trigger("click");
expect(callback.getCall(0).args[0]).to.be.equal("123");
expect(callback.getCall(1).args[0]).to.be.equal("456");
expect(callback.callCount).to.be.equal(2);
1
2
3
4
5
6
7
8

测试 axios

Test4.vue

<template>
<span >{{user}}</span>
</tempalte>
1
2
3
{
  data(){
    return {user:''}
  },
  mounted(){
      axios.get('/user').then((res)=>{
          this.user = res.data.user;
      }).catch(err=>{
          console.log(err);
      });
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

test4.spec.js

beforeEach(() => {
  moxios.install();
});
afterEach(() => {
  moxios.uninstall();
});
it("测试axios", (done) => {
  let wrapper = shallowMount(Test4);
  moxios.stubRequest("/user", {
    status: 200,
    response: { user: "jw" },
  });
  moxios.wait(() => {
    expect(wrapper.text()).to.include("jw");
    done();
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

测试 vuex

Store.vue

<template>
  <div>
    <span>{{ this.$store.state.username }}</span>
    <button @click="clickMe">点击</button>
  </div>
</template>
<script>
// 测试vuex 从两个方向 测试ui 测试功能
import { mapState, mapActions } from "vuex";
export default {
  computed: {
    ...mapState(["username"]),
  },
  methods: {
    ...mapActions(["set_username"]),
    clickMe() {
      this["set_username"]("jw");
    },
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

store.js

export default {
  state: {
    username: "zfpx",
  },
  mutations: {
    set_username(state, username) {
      state.username = username;
    },
  },
  actions: {
    set_username({ commit }, username) {
      commit("set_username", username);
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

main.js

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import config from "./store";
import Vuex from "vuex";

Vue.use(Vuex);
let store = new Vuex.Store(config);

Vue.config.productionTip = false;

new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount("#app");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

store.spec.js

import { shallowMount, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import Store from "@/components/Store.vue";
let localVue = createLocalVue();
localVue.use(Vuex); //防止用例之间的污染
let state;
let store;
let actions;
let callback = jest.fn();
describe("测试vuex 能否在页面中使用", () => {
  state = { username: "jw" };
  actions = {
    set_username: callback,
  };
  beforeEach(() => {
    state = { username: "jw" };
    store = new Vuex.Store({
      state,
      actions,
    });
  });
  it("state能否正常显示到页面中", () => {
    let wrapper = shallowMount(Store, {
      localVue,
      store,
    });
    expect(wrapper.text()).toContain("jw");
  });
  it("点击按钮时action能否正常触发", () => {
    let wrapper = shallowMount(Store, {
      localVue, //提供测试的构造函数vue
      store,
    });
    wrapper.find("button").trigger("click");
    expect(callback).toHaveBeenCalled();
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

将 store 中的部分内容放到 main.js 中可以改造得更简洁

import { createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import config from "@/store";
let localVue = createLocalVue();
localVue.use(Vuex); //防止用例之间的污染

describe("测试vuex 能否在页面中使用", () => {
  let store;
  beforeEach(() => {
    store = new Vuex.Store(config);
  });
  it("state能否正常显示到页面中", () => {
    expect(store.state.username).toBe("zfpx");
    jest.useFakeTimers(); //创建一个模拟的定时器,会把异步代码立刻返回
    store.dispatch("set_username", "newName");
    jest.runAllTimers(); //把timer执行
    expect(store.state.username).toBe("newName");
    jest.useRealTimers();
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.e2e

mocha + chai jest

# 3.场景问题

  • 全屏场景
  • 边界场景
  • 浏览器窗口变化
  • 页面缩放