1 // 引用vue和需要测试的组件 2 import Vue from 'vue' 3 import HelloWorld from '@/components/HelloWorld' 4 // 创建测试套件,一个测试组件写一个测试套件 5 describe('HelloWorld.vue', () => { 6 // 测试用例,用来测试不同的方法或者显示的内容 7 it('should render correct contents', () => { 8 const Constructor = Vue.extend(HelloWorld) 9 const vm = new Constructor().$mount() 10 // 判断页面中是否有msg所渲染出来的内容 11 // 等价document.querySelector('.hello h1') 12 expect(vm.$el.querySelector('.hello h1').textContent) 13 .to.equal('Welcome to Your Vue.js App') 14 }) 15 })
模仿微博的自动化测试代码:
1 import Vue from 'vue' 2 import SineWeibo from '@/components/SinaWeibo' 3 import {mount} from 'vue-test-utils' 4 // 创建测试套件 5 describe('SinaaWeibo.vue',()=>{ 6 // 创建测试实例------点击发布按钮,发布新内容&&个人微博数量+1 7 it('点击发布按钮,发布新内容&&个人微博数量+1',()=>{ 8 // 找到要测试的组件实例,进行挂载 9 const wrapper=mount(SineWeibo); 10 // 找到发表微博的输入内容 11 const textArea=wrapper.find('.weibo-publish-wrapper textarea') 12 // 发布按钮 13 const buttonOfPublish = wrapper.find('.weibo-publish-wrapper button') 14 // weiboNews是从mock中获取的数据,是微博的初始值 15 const lengthOfWeiboNews = wrapper.vm.weiboNews.length; 16 // 右边的关注/粉丝/微博和其数量:2是微博数 17 const countOfMyWeibo=wrapper.vm.profileData[2].num; 18 // 模拟新的微博内容 19 wrapper.setData({newWeiboContent:{ 20 imgUrl: '../../static/image/profile.jpg', 21 name: 'Lee_tanghui', 22 resource: '刚刚 来自 网页版微博', 23 content: '欢迎来到我的微博', 24 images: [] 25 }}) 26 // 触发发布按钮事件 27 buttonOfPublish.trigger('click') 28 // 获取增加后的微博条数,和右边的微博数量 29 const lengthOfWeiboNewsAfterPublish=wrapper.vm.weiboNews.length; 30 const countOfMyWeiboAfterPublish=wrapper.vm.profileData[2].num 31 //断言: 发布后的微博条数是在原来的基础上+1; 32 expect(lengthOfWeiboNewsAfterPublish).to.equal(lengthOfWeiboNews + 1); 33 //断言: 个人微博数量是在原来的基础上+1; 34 expect(countOfMyWeiboAfterPublish).to.equal(countOfMyWeibo + 1); 35 }) 36 // 测试实例:当文本框无内容时候,不能发表微博到列表,且弹出提示框 37 it('当文本框中无内容时, 不能发布空微博到微博列表, 且弹出提示框',()=>{ 38 const wrapper=mount(SineWeibo); 39 // 找到发布框内容 40 const textArea = wrapper.find('.weibo-publish-wrapper textarea'); 41 // 找到发布按钮 42 const buttonOfPublish = wrapper.find('.weibo-publish-wrapper button'); 43 // 获取下方微博条数 44 const lengthOfWeiboNews = wrapper.vm.weiboNews.length; 45 // 获取右边微博数 46 const countOfMyWeibo = wrapper.vm.profileData[2].num; 47 // 设置发表微博,但是content的内容为空 48 //设置textArea的绑定数据为空 49 wrapper.setData({newWeiboContent: { 50 imgUrl: '../../static/image/profile.jpg', 51 name: 'Lee_tanghui', 52 resource: '刚刚 来自 网页版微博', 53 content: '', 54 images: [] 55 }}); 56 // 触发发布按钮 57 buttonOfPublish.trigger('click'); 58 // 获取发表后的下方微博条数 59 const lengthOfWeiboNewsAfterPublish = wrapper.vm.weiboNews.length; 60 // 获取发表后的下右边微博数 61 const countOfMyWeiboAfterPublish = wrapper.vm.profileData[2].num; 62 // 断言:发表前后的微博条数是相等的 63 //断言: 没有发布新内容 64 expect(lengthOfWeiboNewsAfterPublish).to.equal(lengthOfWeiboNews); 65 //断言: 个人微博数量不变 66 expect(countOfMyWeiboAfterPublish).to.equal(countOfMyWeibo); 67 }); 68 69 70 // 测试实例:当点击"关注", 个人头像下关注的数量会增加1个, 且按钮内字体变成"取消关注 71 it('当点击"关注", 个人头像下关注的数量会增加1个, 且按钮内字体变成"取消关注',()=>{ 72 // 获取wrapper 73 const wrapper = mount(SineWeibo); 74 // 获取“关注”button 75 const buttonOfAddAttendion = wrapper.find('.add'); 76 // 获取右边的关注数量 77 const countOfMyAttention = wrapper.vm.profileData[0].num; 78 // 触发“关注”button 79 buttonOfAddAttendion.trigger('click'); 80 // 获取右边的关注数量 81 const countOfMyAttentionAfterClick = wrapper.vm.profileData[0].num; 82 // 断言1:右边的关注数量等于原来的+1; 83 expect(countOfMyAttentionAfterClick).to.equal(countOfMyAttention + 1); 84 // 断言2:button的text变为“取消关注” 85 expect(buttonOfAddAttendion.text()).to.equal('取消关注'); 86 87 }) 88 89 90 // 测试实例:当点击"取消关注", 个人头像下关注的数量会减少1个, 且按钮内字体变成"关注 91 it('当点击"取消关注", 个人头像下关注的数量会减少1个, 且按钮内字体变成"关注',()=>{ 92 const wrapper=mount(SineWeibo) 93 // 找到“取消关注”按钮 94 const buttonOfUnAttendion = wrapper.find('.cancel'); 95 // 获取右边关注人数 96 const countOfMyAttention = wrapper.vm.profileData[0].num; 97 // 触发“取消关注”按钮的点击事件 98 buttonOfUnAttendion.trigger('click'); 99 // 获取右边关注人数 100 const countOfMyAttentionAfterClick = wrapper.vm.profileData[0].num; 101 // 断言1:右边的人数为原来的人数-1 102 expect(countOfMyAttentionAfterClick).to.equal(countOfMyAttention - 1); 103 // 断言2:cancel的text变成"关注" 104 expect(buttonOfUnAttendion.text()).to.equal('关注'); 105 }) 106 107 it('当点击"收藏"时, 我的收藏会增加1个数量, 且按钮内文字变成"已收藏"', () => { 108 const wrapper = mount(SineWeibo); 109 const buttonOfCollect = wrapper.find('.uncollectedWeibo'); 110 const countOfMyCollect = Number(wrapper.find('.collect-num span').text()); 111 112 //触发点击事件 113 buttonOfCollect.trigger('click'); 114 const countOfMyCollectAfterClick = Number(wrapper.find('.collect-num span').text()); 115 116 //断言: 我的收藏数量会加1 117 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect + 1); 118 //断言: 按钮内文字变成已收藏 119 expect(buttonOfCollect.text()).to.equal('已收藏'); 120 }) 121 122 it('当点击"已收藏"时, 我的收藏会减少1个数量, 且按钮内文字变成"收藏"', () => { 123 const wrapper = mount(SineWeibo); 124 const buttonOfUnCollect = wrapper.find('.collectedWeibo'); 125 const countOfMyCollect = Number(wrapper.find('.collect-num span').text()); 126 127 //触发点击事件 128 buttonOfUnCollect.trigger('click'); 129 const countOfMyCollectAfterClick = Number(wrapper.find('.collect-num span').text()); 130 131 //断言: 我的收藏数量会减1 132 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect - 1 ); 133 //断言: 按钮内文字变成已收藏 134 expect(buttonOfUnCollect.text()).to.equal('收藏'); 135 }); 136 137 it('当点击"已收藏"时, 我的收藏会减少1个数量, 且按钮内文字变成"收藏"', () => { 138 const wrapper = mount(SineWeibo); 139 const buttonOfUnCollect = wrapper.find('.collectedWeibo'); 140 const countOfMyCollect = Number(wrapper.find('.collect-num span').text()); 141 142 //触发点击事件 143 buttonOfUnCollect.trigger('click'); 144 const countOfMyCollectAfterClick = Number(wrapper.find('.collect-num span').text()); 145 146 //断言: 我的收藏数量会减1 147 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect - 1 ); 148 //断言: 按钮内文字变成已收藏 149 expect(buttonOfUnCollect.text()).to.equal('收藏'); 150 }); 151 152 it('当点击"赞", 我的赞会增加1个数量, 且按钮内文字变成"取消赞"', () => { 153 const wrapper = mount(SineWeibo); 154 const buttonOfLike = wrapper.find('.dislikedWeibo'); 155 const countOfMyLike = Number(wrapper.find('.like-num span').text()); 156 157 //触发点击事件 158 buttonOfLike.trigger('click'); 159 const countOfMyLikeAfterClick = Number(wrapper.find('.like-num span').text()); 160 161 //断言: 我的赞会增加1个数量 162 expect(countOfMyLikeAfterClick).to.equal(countOfMyLike + 1); 163 //断言: 按钮内文字变成取消赞 164 expect(buttonOfLike.text()).to.equal('取消赞'); 165 }); 166 167 it('当点击"取消赞", 我的赞会减少1个数量, 且按钮内文字变成"赞"', () => { 168 const wrapper = mount(SineWeibo); 169 const buttonOfDislike = wrapper.find('.likedWeibo'); 170 const countOfMyLike = Number(wrapper.find('.like-num span').text()); 171 172 //触发点击事件 173 buttonOfDislike.trigger('click'); 174 const countOfMyLikeAfterClick = Number(wrapper.find('.like-num span').text()); 175 176 //断言: 我的赞会增加1个数量 177 expect(countOfMyLikeAfterClick).to.equal(countOfMyLike - 1); 178 //断言: 按钮内文字变成取消赞 179 expect(buttonOfDislike.text()).to.equal('赞'); 180 }); 181 })
模仿微博的vue 代码:
<template> <div class="weibo-page"> <nav> <span class="weibo-logo"></span> <div class="search-wrapper"> <input type="text" placeholder="大家正在搜: 李棠辉的文章好赞!"> <img v-if="!iconActive" @mouseover="mouseOverToIcon" src="../../static/image/search.png" alt="搜索icon"> <img v-if="iconActive" @mouseout="mouseOutToIcon" src="../../static/image/search-active.png" alt="搜索icon"> </div> </nav> <div class="main-container"> <aside class="aside-nav"> <ul> <li :class="{ active: isActives[indexOfContent] }" v-for="(content, indexOfContent) in asideTab" :key="indexOfContent" @click="tabChange(indexOfContent)"> <span>{{content}}</span> <span class="count"> <span v-if="indexOfContent === 1" class="collect-num"> ( <span>{{collectNum}}</span> ) </span> <span v-if="indexOfContent === 2" class="like-num"> ( <span>{{likeNum}}</span> ) </span> </span> </li> </ul> </aside> <main class="weibo-content"> <div class="weibo-publish-wrapper"> <img src="../../static/image/tell-people.png"></img> <textarea v-model="newWeiboContent.content"></textarea> <button @click="publishNewWeiboContent">发布</button> </div> <div class="weibo-news" v-for="(news, indexOfNews) in weiboNews" :key="indexOfNews"> <div class="content-wrapper"> <div class="news-title"> <div class="news-title__left"> <img :src="news.imgUrl"> <div class="title-text"> <div class="title-name">{{news.name}}</div> <div class="title-time">{{news.resource}}</div> </div> </div> <button class="news-title__right add" v-if="news.attention === false" @click="attention(indexOfNews)"> <i class="fa fa-plus"></i> 关注 </button> <button class="news-title__right cancel" v-if="news.attention === true" @click="unAttention(indexOfNews)"> <i class="fa fa-close"></i> 取消关注 </button> </div> <div class="news-content">{{news.content}}</div> <div class="news-image" v-if="news.images.length"> <img v-for="(img, indexOfImg) in news.images" :key="indexOfImg" :src="img"> </div> </div> <ul class="news-panel"> <li :class="{uncollectedWeibo: !news.collect, collectedWeibo: news.collect}" @click="handleCollect(indexOfNews)"> <i class="fa fa-star-o" :class="{collected: news.collect }"></i> {{news.collect ? "已收藏" : '收藏'}} </li> <li> <i class="fa fa-external-link"></i> 转发 </li> <li> <i class="fa fa-commenting-o"></i> 评论 </li> <li :class="{dislikedWeibo: !news.like, likedWeibo: news.like}" @click="handleLike(indexOfNews)"> <i class="fa fa-thumbs-o-up" :class="{liked: news.like}"></i> {{news.like ? '取消赞' : '赞'}} </li> </ul> </div> </main> <aside class="aside-right"> <div class="profile-wrapper"> <div class="profile-top"> <img src="../../static/image/profile.jpg"> </div> <div class="profile-bottom"> <div class="profile-name">Lee_tanghui</div> <ul class="profile-info"> <li v-for="(profile, indexOfProfile) in profileData" :key="indexOfProfile"> <div class="number">{{profile.num}}</div> <div class="text">{{profile.text}}</div> </li> </ul> </div> </div> </aside> </div> <footer> Wish you like my blog! --- LITANGHUI </footer> </div> </template> <script> //引入假数据 import * as mockData from '../mock-data.js' export default { created() { //模拟获取数据 this.profileData = mockData.profileData; this.weiboNews = mockData.weiboNews; this.collectNum = mockData.collectNum; this.likeNum = mockData.likeNum; }, data() { return { iconActive: false, asideTab: ["首页", "我的收藏", "我的赞"], isActives: [true, false, false], profileData: [], weiboNews: [], collectNum: 0, likeNum: 0, newWeiboContent: { imgUrl: '../../static/image/profile.jpg', name: 'Lee_tanghui', resource: '刚刚 来自 网页版微博', attention:false, content: '', images: [] }, } }, methods: { mouseOverToIcon() { this.iconActive = true; }, mouseOutToIcon() { this.iconActive = false; }, tabChange(indexOfContent) { this.isActives.forEach((item, index) => { index === indexOfContent ? this.$set(this.isActives, index, true) : this.$set(this.isActives, index, false); }) }, publishNewWeiboContent() { if(!this.newWeiboContent.content) { alert('请输入内容!') return; } const newWeibo = JSON.parse(JSON.stringify(this.newWeiboContent)); this.weiboNews.unshift(newWeibo); this.newWeiboContent.content = ''; this.profileData[2].num++; }, attention(index) { this.weiboNews[index].attention = true; this.profileData[0].num++; }, unAttention(index) { this.weiboNews[index].attention = false; this.profileData[0].num--; }, handleCollect(index) { this.weiboNews[index].collect = !this.weiboNews[index].collect; this.weiboNews[index].collect ? this.collectNum++ : this.collectNum--; }, handleLike(index) { this.weiboNews[index].like = !this.weiboNews[index].like; this.weiboNews[index].like ? this.likeNum++ : this.likeNum--; } } } </script> <style lang="less"> * { margin: 0; padding: 0; box-sizing: border-box; } .weibo-page { display: flex; flex-direction: column; } nav { display: flex; height: 60px; line-height: 60px; background-color: #f0f0f0; align-items: center; .weibo-logo { margin-left: 13%; 100px; height: 28px; background: url("../../static/image/WB_logo.png") no-repeat; cursor: pointer; } .search-wrapper { display: flex; position: relative; 25%; input { 100%; height: 24px; padding-left: 10px; } img { position: absolute; right: 10px; top: 4px; 20px; height: 20px; cursor: pointer; } } } .main-container { display: flex; justify-content: center; background-color: #000; padding-top: 16px; color: #fff; .aside-nav { 11.31%; ul { list-style: none; li { height: 34px; line-height: 34px; padding-left: 10px; cursor: pointer; font-weight: bold; background-color: rgb(19, 19, 19); } li:hover { background-color: rgb(66, 66, 66); } .active { background-color: rgb(66, 66, 66); } } } .weibo-content { 45.41%; color: #000; .weibo-publish-wrapper { background: #fff; position: relative; padding: 10px 10px 54px 10px; textarea { display: block; 100%; height: 95px; padding: 10px; } button { position: absolute; right: 10px; bottom: 10px; 82px; height: 30px; background: #ff8140; border: 1px solid #f77c3d; color: #fff; box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25); cursor: pointer; } } .weibo-news { background: #fff; margin: 10px 0; .content-wrapper { padding: 20px 20px 4px 20px; } .news-title { display: flex; justify-content: space-between; .news-title__left { display: flex; img { 50px; height: 50px; vertical-align: text-top; } .title-text { display: flex; flex-direction: column; justify-content: space-around; margin-left: 10px; } .title-name { color: #333; font-size: 14px; font-weight: 700; } .title-time { color: #808080; margin-bottom: 2px; font-size: 12px; } } .news-title__right { align-self: center; 58px; height: 22px; line-height: 22px; background: #fff; border: 1px solid #d9d9d9; cursor: pointer; img { position: relative; left: 1px; top: 2px; } i { color: #ff8140; } } .cancel { 86px; } } .news-content { padding: 10px 0 10px 60px; font-size: 14px; } .news-image { padding: 10px 0 0 60px; img { 110px; height: 110px; margin-right: 10px; } } .news-panel { display: flex; list-style: none; border-top: 1px solid #f2f2f5; li { 25%; height: 22px; line-height: 22px; margin: 7px 0; text-align: center; border-left: 1px solid #f2f2f5; color: #808080; cursor: pointer; .collected { color: #ff8140; } .like { color: #ff8140; } } } } } .aside-right { 17.41%; margin-left: 10px; .profile-top { position: relative; height: 75px; background: url('../../static/image/profile-bg.jpg'); img { position: absolute; bottom: -30px;; left: 50%; margin-left: -30px; 60px; height: 60px; border-radius: 50%; } } .profile-bottom { height: 118px; padding-top: 30px; background: #fff; color: #000; .profile-name { text-align: center; font-weight: 700; } .profile-info { display: flex; list-style: none; padding-top: 10px; li { 33.333%; border-left: 1px solid #f2f2f5; cursor: pointer; div { text-align: center; } .number{ font-weight: 700; } } } } } } footer { height: 100px; line-height: 100px; text-align: center; font-size: 12px; font-weight:700; } </style>