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%; width: 100px; height: 28px; background: url("../../static/image/WB_logo.png") no-repeat; cursor: pointer; } .search-wrapper { display: flex; position: relative; width: 25%; input { width: 100%; height: 24px; padding-left: 10px; } img { position: absolute; right: 10px; top: 4px; width: 20px; height: 20px; cursor: pointer; } } } .main-container { display: flex; justify-content: center; background-color: #000; padding-top: 16px; color: #fff; .aside-nav { width: 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 { width: 45.41%; color: #000; .weibo-publish-wrapper { background: #fff; position: relative; padding: 10px 10px 54px 10px; textarea { display: block; width: 100%; height: 95px; padding: 10px; } button { position: absolute; right: 10px; bottom: 10px; width: 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 { width: 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; width: 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 { width: 86px; } } .news-content { padding: 10px 0 10px 60px; font-size: 14px; } .news-image { padding: 10px 0 0 60px; img { width: 110px; height: 110px; margin-right: 10px; } } .news-panel { display: flex; list-style: none; border-top: 1px solid #f2f2f5; li { width: 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 { width: 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; width: 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 { width: 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>