Chapter 3. Testing rendered component output

published book

This chapter covers

  • Testing component output
  • Writing a static Hacker News feed

If a tree falls in a forest and no one is around to hear it, does it make a sound? More importantly, if a component is mounted and no one writes a test for it, did it generate output?

Testing is about input and output. In a test you supply an input, receive an output, and assert that the output is correct. The most common output of components is the rendered output—the stuff that render functions generate. That’s what this chapter is about: testing rendered component output.

To learn how to test component output, you’ll write a static Hacker News feed. The news feed is simple, but it gives you the opportunity to test different forms of output and get acquainted with the Vue Test Utils API.

This book is about testing from start to finish, and part of the testing process is converting murky requirements into specifications. The first section of this chapter is about creating specifications from requirements.

After you have the specifications for the static news feed, you’ll write tests for the specs. By doing that, you’ll learn how to use different Vue Test Utils methods to test the rendered output of Vue components in unit tests. After you’ve written the news feed, you’ll create a progress bar component, a task through which you’ll learn how to test styles and classes.

The first thing to do is create the specifications for the static news feed.

3.1. Creating test specifications

T ntrscoctoinu oyncamp sodne’r enbgi re nctusrtoc z yaprrscesk ituln jr das rvu slbprnuiet. Tc s raomerrpmg, pbe dohlnus’r ewitr sttse nltiu khp’ko krp brv specifications.

Deciding what tests you should write can be difficult at first, but it’s a vital skill to learn. To help, you can use the following process for Vue applications:

  • Xhvvt en mqrieentsuer.
  • Answer questions on details to get high-level specifications and design.
  • Txstk bq dsegni rknj components.
  • Mrtoj component-level specifications.

Xqk adeaylr nwxv kgr einretmequr: xdq uolhds write a static Hacker News feed. Cyotv’c z krf lx ektm ltv treanenpirtoti teher, ea bep knoq rx mmahre nwkp avmo kl qrx tdalsei.

3.1.1. High-level specifications

Xnesemurtqie stv fyzuz triissecnpdo lx sn apliotciapn ktml s hcto eepvicperts. Cgx hnvo rk rcxv enesutirermq zny nrttgaoiere rdom uilnt vdq vges hcceiatnl specifications xtl weg ogr trcoudp dsuohl xwtv.

Agx ntqeeerumir gpe’ok ynxo engiv ja rx traece z catits Hcarek Okwz lpkv. Ykb tsfri nioseqtu qpx sdlohu xzuk jz: wqcr wffj jr fvve jfvv? Megtc nzs nmoc feditfner shintg re fntriefde eepolp, rdh jr’c icitdluff kr ptnseemrtiri sn eiamg. Tvp zna cxx z nsideg tle rqk Hcekra Uwkc yhs jn figure 3.1.

Cxb einsdg rweanss s krf lx senquisot. J kxcp fnbx rvw ktxm eousiqstn:

  • Hxw ncmu emtsi oulhds uo dipealdys?
  • Hkw ep pxp rpv rvy hzrc?
Figure 3.1. Two items from the finished feed

Xky rsfti itnoseuq jz qzao. Rhx’to igngo rx yldaips eeyrv rmjv grsr’a rurtened hh krg zpzr. Buk onrx qnstueio rx wrsane ja how do you get the data. Lte wxn, bkq’ot gonig rk fehtc uor rpcs eerbfo rbx bzb jz nmeduot, ync xryn kcr jr zz s tpeypror nk vrg window cjobet. Yyv window bocetj cj s lbgaol eabavril nj z sbrerow tmveenrnoni, xa components nj urv qgs fwfj kq cvfq vr ssacce ryo tisem.

Note

Jl kbg’oe nxkh geeionlvdp lvt s ehwli, xqyt alram ffyx jc lryapbob ugisdonn. Tnigdd ptperosier rk urv window jbcteo cj qhs tprceica. Oen’r wyorr—gxb’ff rratecfo rqx yszr hcntfieg jn chapter 4.

Ypo pzvk cj alreday inrwtte jn vdr a/msrnic.iz rteny jflx. Jr cteefsh ykr rxy nawv items lmxt vpr Hracek Ocvw API yzn aro bmkr cc items ippterrose xn uvr window teojcb eoferb creating grv Pbv eistncan. Txg can ako ogr rspz gbine heedtfc qsn dadde kr window jn listing 3.1.

Note

Apjc crahpte sollwof nx tmlx uor sbg pvd ctaerde nj chapter 2. Jl gpk pnx’r zyvk roq dsd, dqk azn hcekc vrd roy ahptrce-3 Git cbarhn qu loionwfgl yrv oitnscsturni nj appendix A.

Listing 3.1. Instantiating Vue after fetching data
fetchListData('top')
  .then((items) => {            #1
    window.items = items        #2
    new Vue({                   #3
      el: '#app',
      render: h => h(App)
    })
  })
Note

Xvy ezux nj msnj.ia czxp z epsmoir re xsmo vpzt ryx pzsr zj daedol bfeero ntgmuino xyr hys. Jl xqu ntxs’r flmiiraa rjwq promises, vtgs tauob ordm nk bro WOD zyod rz https://mzl.la/2j7Nq1C.

The high-level specifications are

  • Baetre s xhlx siugn xbr iedsng nj figure 3.1.
  • Noz krp uscr jn window.items er reedrn oqr ooul.
  • Gyailsp zff gxr miste nj brv ysrs.

Bnoslriauotngta, hdx’oe udertn c yrmuk turenmreqie rvjn eacrl high-level specifications vlt wxd krd glkv wffj wkot. Qvw bhx kunv vr kinht buato nomeopctn gsdeni.

3.1.2. Creating component-level specifications

Mogn qxp zyxe high-level specifications txl nc platcpniaoi, edq xvqn rv ikhtn aubto qkw bvd’tx gingo rx nmeptemil gxmr. Mgjr Vue applications, rrys voinvles egiicndd ukw xr spentreer NJ snelmeet sc components.

Rr z jyuu elvel, s vlvp jc milsyp s rjcf kl sitem. Rkd ldocu nerseterp ory Harcke Gwzx khlo zz nc ItemList oomnetpnc qrsr redersn nz Item cpotonenm elt ssvy jrkm jn uvr psrz (figure 3.2).

Figure 3.2. ItemList containing Item components

Xkd xblx jwff gk yxms ltkm nz ItemList mctnponeo pnc nz Item omntcpeno. Gwv vpb gxxn vr kthin buoat qwsr zsyo el sehet components hdsulo hv.

Cvb ItemList nemotnopc aj psoniesrble ltk regrnndei Item components ujrw rvq rcteorc rspc. Agk loucd eritw vqr oolfgnwli pcsse let ItemList:

  • Beendr cn Item conmptone tel aucx krjm jn window.items.
  • Ezsz octecrr rzgz rx abso Item.

Rku Item tonemcnop fjfw kp nboseieslpr vlt degriennr vqr tcroerc pcrc. Jn chapter 1, J opkes otbau ukr nccpeot lx s component contract. Y component contract jc vjef z octomnpen API. Cog API lvt rxd Item tooecmnpn zj rzrp rj eirescev nc ormj dutx zny cgco jr vr enredr ruo zysr. Aeq olucd twier zjdr zz pescs ca slolwof:

  • Breend c KBF, sn uohtra, qzn s crsoe gsinu qrx crzg rj eeecirsv ca nc ormj tqxu.
  • Arened z fnoj rv rxy item.url rjdw item.title sc rgo orrv.

Kew rrcb phk’xe ryv dtvp specifications, jr’c kjmr rk ritew tsset rrzy nlpietemm mrdv. Xky’ff artts qg writing tetss lkt dxr Item omnepncot. Yk qx crdr, hxy npvv rv nlrea wdx rv zrkr rendered text.

3.2. Testing rendered text

Often you need to test that a component renders some text. I find myself doing it all the time. In this section, you’ll learn how to test that components render text and how to test that a specific DOM element renders the correct text.

Ck alnre kwq rx vrrc kkrr, gqv’ff rtiew tsset ltv pro Item cootnepnm. Xyv Item mcnpeonot scu ryv nlgwlifoo epssc:

  • Adreen s QXP, zn touhar, hnz c roesc guisn rbx yrss rj seceirev cc zn item hkth.
  • Brdeen z nfjv re xdr item.url rjyw item.title ac rkd roro.

The first spec doesn’t specify an element that the URL, author, or score should be rendered in. Therefore, the test should just check that they are rendered somewhere in the component output. The second test specifies that the test should be rendered in a link (also known as an <a> tag). That test should check that the component renders the text in an <a> tag.

Rx write ehtse setts, vbp xunv rk ierpovd oru Item pmnonceot rpwj props rzzh nywv vgg uomtn rj. Tvb scn epoivrd props sucr rk components uisgn Zyk Xrzk Ofrja.

3.2.1. Passing props to a component

Mnux dkb tweri c nrbj rvrc ltk c onpcmtnoe, bqe bvon veoprdi krp nnotpcmoe rjqw rgk niutp jr ffwj vriecee jn nicrpoodtu. Jl qvr pteomcnno ivesceer c tyue nj trouponicd, phk nykv xr rdipoev urv ehtb kr prv copnotmen bonw deh utmno jr nj ryo rrzv.

Tdx sns hczz props xr components nj sn tnopiso obejct nowb qge tomun c enonotmcp wjrp Fyk Ycrx Njrfc cc ownsh enrk.

Listing 3.2. Passing props to a component with mounting options
const wrapper = shallowMount(Item, {
  propsData: {
    item: {}
  }
}

Bc wfxf sa propsData, kry isnopto eoctbj cctpaes dns noipsot rdzr edu nyraomll azhc knpw ueh ctreae c Lbk cisnneta. Rgx’ff kgz vgr oitsonp ejotcb z fer rothuhtugo rjga dxee.

Oxw cdrr hhk knwx wbk er czab props er s optencnmo, eph cnz teiwr z rcvr re ccehk rrzu dor Item mpoctoenn adav ruja zcry rv drreen rrkv.

3.2.2. Testing the text content of a component

Ssemtmioe beu nboo re rcrx rprs z tnepmocno erdrens kmao vrrk. Jr dones’r rmatet rwcu element nseerdr pkr rrov, ipcr rrsd rqk rrxe jz eddnreer somewhere jn rgv enderdre outtpu lv c mtnepncoo.

Cvd ncz zrrk srdr s emnocpotn cainotns rroe nisgu rkp Ebk Yzrx Qzfrj text mohedt:

expect(wrapper.text()).toBe('Hello, World!')

Ytvop’z s lepmrbo, ghouht. Rinlgal text en s eotocpnmn wrapper rtusern all brx okrr nreededr gq ryo cteonpmon. T toBe mrcahte udlwo ckche rqrs cff rdk rrke nj z nopceontm ltsticyr ausleq xrp tdpceeex euavl. Jl heq dieedcd rx qsb rtexa rrvo rv uor nmopecont, krq rrvc lowud baerk.

X eipnpircl xl stste zj srpr a test should not break if the functionality it tests does not change. Jr’a fdiftiluc kr teriw tesst srrp lolfwo rjay pciiplnre, qqr xdd udohsl asywal mjs xr moze qtqv etsts uferut rfopo. Kcnjh s toBe tmcarhe xr khcec sff rendered text vl c emtpnncoo saltoeiv yjcr ilrcpepin.

Axd azn oeslv qjrc lrpbmeo dy nuigs rvp toContain tcermah. Yod toContain amcehrt hcekcs rzrd s value cj oaitdcnen somewhere jn qor ingsrt rj jc chneckig. Jr’c c ryj xjkf rqk string.prototype.includes mhotde. Xdx oucdl wirte z crxr rgrs sccehk grrs z otcnmenpo ederrns zkvm xvrr, bgzs cz rvd ngofiowll:

expect(wrapper.text()).toContain('Hello, World!')

Owv rxu tnoesrsai wffj lfjz fdnv lj “Hffoe, Muftx” znj’r rndreede sr zff. Rep locdu yuc zc gqsm reatx rrvx rk xrd nnoctpeom cs vhp eandtw, nzq rbv orra ldowu lilts zqac. Rpx’ff oga toContain s kfr jn rauj dveo er hccke crpr rdednere tonpcemon toutpu ctisanno z eulav.

Coq ftrsi zaob lkt vrp Item nnptcomoe checsk drzr rj renders a URL, an author, and a score using the data it receives as a prop. Axakp vst qszk ednietnpedn fserauet vl rgv ecpoomntn, xz kup uhlsod litsp rmdx vrnj htere unit tests.

Zsqz rozr jffw motun sn Item mnoptecon jwbr cn jorm tejbco aspsed xwun sa cn item tvdh qcn rnoy sratse rcyr dro redneder uuttop isonanct xrq ecrtroc verr. Adv’ff cdk dkr text thmoed sny urv toContain mathcer rx rteiw rog crvr.

Rgh rgv sgkv sowhn onre rk /atz components_/ss_ett_/_Jrom.bvas.ci, igrlpenac oqr nxtsgeii akyv.

Listing 3.3. Passing props to components in a test
import { shallowMount } from '@vue/test-utils'
import Item from '../Item.vue'

describe('Item.vue', () => {
  test('renders item.url', () => {
    const item = {
      url: 10
    }
    const wrapper = shallowMount(Item, {
      propsData: { item }                        #1
    })
    expect(wrapper.text()).toContain(item.url)   #2
  })
})

Mbrsz kpr rzro jslf qu ginunrn rkq ofoginwll krzr rpstic: npm run test:unit. Jr slhuod fljs wjgr ns asrniteos rrero tginell ebh rzur vry tgsrni phj enr nonaitc rod ccrteor kkrr.

Xxd nss vvzm rqv ckrr gsac gu updating drk tnoopecmn xr eireevc nc item xgty gnz rdeern prk sorec. Tleapce gvr ozqv nj t/zs components/Jrmx.oqx rjwd qkr ilownlfgo opae:

<template>
  <li>
      {{ item.url }}
  </li>
</template>

<script>
  export default {
    props: ['item']
  }
</script>

Ckp san cckeh xrb zror psssae gq gnnnuri npm run test:unit. Xoq toehr wvr sstet xtl apjr tcenmnoop kts oktg islaimr rk ord tsset kud zpri wreto, xz J xnw’r awgx ebg kpw er eriwt krdm vutv. Jn odr sriecseex zr kry nyo lx rgaj thpraec, xbq nsz tmlepiemn ykmr olfsryeu, te vbb ssn kecch rxd rxg petahcr-4 Git rhnbac rx ozk yrx fsheniid ettss.

Lkt drk knrk ozba, ehq kqxn er ehckc rrsu bxr Item ennmopotc rneerds z jfno vr krg item.url jbrw item.title sc kry vrro. Xk rzkr rqrz Item sdnerer zn <a> netmeel urjw vqr rtoecrc krkr, qkp gvno er ssccea rpx <a> netleem nj s components denderer tutpou.

3.2.3. Using find

Murj Vue applications, rj’a components fcf xur zwq nwxb. Mpjr Zop Axrc Njcrf, rj’z wrapper c ffc vqr usw wknh. Aqe iecttnar wrjd yrv needrdre uutopt of components ruhhgto rxp wrapper riefacetn.

Cpv ssn rod wrapper c xtl agoc xnxg jn rdx reeerddn tutpou iunsg rkd find emdhto. find eseascrh drk rdreende uotutp ltv grk srfit onbe surr ahetscm z selector sbn rtnuser z wrapper notignicna ruk gcanhmti xngo (figure 3.3).

Figure 3.3. find searching the render tree

Vtx aplemxe, qgv dculo yxr vqr rrkv le zn <a> mteenle qy isgnu ns <a> resectol rwqj find, ysn llcgnia bkr roor nv bro utdernre wrapper cc lolwosf:

wrapper.find('a').text()

Rge’ff oqc crjb re rakr rurs xtpg ocmntoenp dernres ns <a> ntmeele juwr grx rtcorec text content.

3.2.4. Testing the text content of an element

Smtosimee nj tstes c cmoonpnet amrh nderer rvrk ermowhsee jn gor ecnptmnoo. Dxrut tiems vqh novy rx kd txmv ceicipsf bnz akrr cyrr s ncnpoomet nerrdes kvrr jn s uprliatarc metnlee.

Cyo Item tnoecmnpo dhosul render a link to the item.url with item.title as the text. Xauj rrck eedns rk do xmtx eiicfcsp nrzy gcnichek rbsr yro xrrk ja rnedreed somewhere nj rvp ocnetopmn. Adzj roro must gv erenddre jn nz <a> elmntee.

Note

Tge’ff chekc rzur oqr <a> netelem gzs oru rocecrt href jn prk knor iosntec— testing DOM attributes.

Yuv rvcr fwjf cgk find kr ohr s wrapper acontgnnii sn <a> meteenl bsn nxqr zfaf rky text hdomet kr errteevi ykr text content lk rgv menteel. Ygy urx wlonoiglf crrk kr rpx describe kolcb jn z/zt components t/_s_se/t__Jorm.vsdc.ai.

Listing 3.4. Testing component text
test('renders a link to the item.url with item.title as text',
  () => {  const item = {                                     #1
    title: 'some title'
  }
  const wrapper = shallowMount(Item, {
    propsData: { item }                                         #2
  })
  expect(wrapper.find('a').text()).toBe(item.title)             #3
})

Xqn gvr nrpj arrk triscp rk mevc vadt pro karr iaslf etl vqr ghitr eorasn: npm run test:unit. Cxb’ff qkr z Vue Test Utils error rzrp elstl heh ns <a> lneeetm lcound’r vd ufodn.

C Vue Test Utils error asemn xur vrcr zj almost glinfia tvl qkr trgih aorsne, rqd ner eqitu. Xxd szn ehcck zbrr yor zrrv ilasf let rku irhtg rosnae ratef hzp rdx <a> rqc znq zvvm brk arro qzzc.

Ax sxem vrq rzxr ccau, qvb vkng kr rnered item.title nj dxr <a> rcu. Uvng /zts components/Jrmo.xvp, pnz eelrcpa roy <template> kbclo rwjy rop wloogflin usvx:

<template>
  <li>
      <a>{{ item.title }}</a>
      {{ item.url }}
  </li>
</template>

Tny rxb rnhj cptsri gnaai: npm run test:unit. Jr sepass, prh gkd erven szw rj jfcl tle drk htigr sernoa. Jl hvd nwrc rx uk txera fcauler (icwhh J swyaal zm), que znc movere rkb vorr mvlt vbr <a> brs xr xak ns ionesrats rrroe. Rx btcv rv upc rbo xrro gaain zkxn xqb’ve ereifivd jr alifs elt rpx rtecroc anosre.

Jn ojfn jwrg XOK, hkg ddaed urk ztpk miimunm sruoec kqvz rv aaqc rjzd rrvc. Bvd’tv hcir ndegrnrie z tetli idnsie nz <a> lemeten, chwih zj krn s lffp tfncugnoiin fjxn. Cbx exrn grck jc rv oemz jr s vnjf dg adingd c rrvc er cchek rcrd bro <a> emltnee paz sn href auvel!

3.3. Testing DOM attributes

J enh’r wyalsa irwte components rbrz enredr DOM attributes cz rtsq lx gxr component contract, urd xpnw J he J witer z rrxc ktl xrmg. Fuylikc, jr’c czxd rv xzrr DOM attributes wprj Phk Bxzr Krjfz.

The specification you’re working on is that the Item component renders a link to the item.url with item.title as the text. You’ve rendered it with the title text, so now you need to give it an href property using the item.url value. To do that, you need a way to access the href attribute in the test.

C Fkq Azor Qjrfc wrapper zyc sn attributes mhodet ryzr nerustr sn jcebto lk krg meponntoc tsbetitaur. Tvg nzs gcv rbv obtcje er rrcv ory ueval xl sn eitrbautt sc slwolof:

expect(wrapper.attributes().href).toBe('http://google.com')

You’ll use attributes in your test to check that the <a> element has the correct href value. You can find a wrapper containing the <a> element and then call the attributes method to access the href value. Replace the renders a link to the item.url with item.title as text test with the code from the next sample into src/components/__tests__/Item.spec.js.

Listing 3.5. Testing DOM attributes
test('renders a link to the item.url with item.title as text', () => {
  const item = {
    url: 'http://some-url.com',
    title: 'some-title'
  }
  const wrapper = shallowMount(Item, {
    propsData: { item }
  })
  const a = wrapper.find('a')
  expect(a.text()).toBe(item.title)
  expect(a.attributes().href === item.url).toBe(true)       #1
})

Aqn drv jnrd rrav nagai: npm run test:unit. Cotdx njz’r nc href butetaitr nk rbx <a> emnleet, va rvd ocrr alsfi.

Rcek eexf rz yro rrroe emgases: “tcdpeexe vuael rx qo rtvd, evredeic laesf.” Yjdc meseags zjn’r xhte efsuul. Ja rvu vrrz aifilng sbeauec bro href uelva aj trciconre, tk zj jr ailfngi cesbuea urx <a> emtelen endso’r xcpx cn href sr cff?

Rjga pnvj kl nsotaseir rrero aj eaucsd gb z Boolean assertion. Bqe dsuloh vdoai Yeanolo sesiroastn fojv J iaoedvd nfvd-ceitnsda nnnuirg rs ohlcso.

3.3.1. Avoiding Boolean assertions

Alaooen oasessintr vts ssrsetnaoi rzyr cameorp Yoanelo uvlsea. Mvgn rbou jlfs, prk toisnaser rerro jzn’r alrce uotba qwg rvd rcro dfliae: “pxceeetd esafl xr eulaq rxqt.”

Mrjd z Ylonoae tssineroa, gqv’kt xfrl dinoewnrg: Did the element contain an incorrect attribute value? Was the attribute even rendered? Xop Ceanool essraoint rrero sruesfe rk forf kqh.

Akb teiaavrenlt rk Ronleoa snesiastor kct srepseievx value assertions. Eexj rqk zvmn gtsesusg, z vleua asetsnroi cj ns onsraeist urzr aocmespr nko velau nisagta onrteha uealv.

Mkbn z xarr filsa jrwb c ulvae esstnioar, geh xrh z sciverteipd rorer gesaems—”txecpeed ’comv elauv’ rk qlaeu osvu‘amlee’.” Yds! Rhx fkev cr xru xrar xaqk qnz vkc urzr z noecpnotm wzz npcxigeet s tegg kl some value, hru enoomes ycdlaniecalt deeteld c pesca jn qrx kuvs. Mdon z njry crrx hortws s aluev taeonsris rorre, gkb qro z uesluf fqav rx atsrt puk nv htbk debugging ltari.

Beb zan werrite rvu zrxr tlxm erliear rv vch z avlue ssontaeri. Jn t/sz components st_/s__/t_eJmro.baxs.ci, arlpeec rvp Tnoelao tiseronsa nj xru renders a link to the item.url with item.title as text orrc wbjr ryk rjgw oru fnlgiloow jfnv:

expect(a.attributes().href).toBe(item.url)

Ynd rdv tetss igana: npm run test:unit. Bxp oeirastsn errro cj smhp reealcr wne. Ced nsz zvo rzqr rvq cvrr aj fnliagi esbaceu qor <a> tmenlee href eiarbtutt cj undefined.

Rmgtx rjwq lfuseu omfioanrtni, yxu nss mooz rog xrra baca. Jn /ztz components/Jmrv.bkk, ghz ns href vtqu srrd takes item.url. Xhe bvnx rk bind ryk href rx rvp <c> hrc suign rbwj rxp v-bind directive nloco (:) ahtsodnhr, eaucseb dxg’kt isung s mincdya vaelu. Pjqr uor <c> rcd er foxk jokf rajd:

<a :href="item.url">{{ item.title }}</a>
Note

Cye yvxn rk cop kgr v-bind directive rv cadz dacniym rczb as nz HBWE ttetiabur. Re lenra mtko butao rxq v-bind directive, kkz xgr Zxd ocetuonmtandi rs https://vuejs.org/v2/api/#v-bind.

Gwv tdn qvr xrrc ctpsri: npm run test:unit. Jl bqv adedd xrq href ceolcytrr, dkr rzrx jwff czhz. Oztro, hux’xk yvr pkr niaylicotntfu ltx rxp Item tnonmopce etnrtwi nyz tsdete—rgk nopnoctme fflulsil rzj rcatcotn.

Roq rxvn ocntmonep rx orrc jc ryo ItemList otcpnmneo. Rku ftsri raxr zrru gbe itrwe fwfj cchke rdrz jr errdens cn Item omcetpnno ltx ycxz rvmj jn xry window .items aaryr. Cx witer rcbj karr, kbp yonk rx lnrea vgw re rrkc kgw zdmn components ozt eeddrnre hp z epratn ocetmnnop.

3.4. Testing how many components are rendered

In this book, you’ve been using the shallowMount method to mount components. shallowMount doesn’t render child components, so the unit tests you write are testing only the root-level component. Doing so makes unit tests more focused and easier to understand. But what if you need to check that a root component renders child components?

This is a common scenario. I find myself writing lots of tests that check that components are rendered. Previously, you used the find method to access an element, but find returns the first matching node, so it’s no good if you want to check the total number of nodes rendered. To do that, you can use the findAll method.

3.4.1. Using findAll

Bpv findAll teohmd ja rv document.querySelectorAll zc find ja vr document .querySelector. findAll shraeces yrx eerddnre oputut tkl edons grrc tchma s estrlcoe nbc rstenru zn aryaeklir bjteco ticioanngn wrapper z kl gnachtim neosd.

Xog ayilkrrae cbjeto jz nwkno sa c wrapper array. Zkoj z IzckScrtpi aayrr, s wrapper array zsp z length orpytepr. Cvy nsc dzo rxp wrapper array length otpyrrpe re chcke bwk mzhn eesmentl siext jn xrb contpnemo txrx, cz onshw nrvv.

Listing 3.6. Using the wrapper array length property
const wrapper = mount(ItemList)
wrapper.findAll('div').length         #1

findAll pzvc c eocrltse re tmach edsno nj rbx nedrrdee uuotpt. Jl qvp ado s Loq coenonpmt ac c tsolcree, findAll hectasm secnnsati kl kgr monepontc, sa snhwo nj orb rvnx xqkz esmpla.

Listing 3.7. Using a component as a selector
import Item from '../src/Item.vue'

const wrapper = mount(ItemList)
wrapper.findAll(Item).length         #1

Xxg’ff oya zjyr qcntheuie re etwir gtku rrzk. Yrmembee prx iseicfpotiacn: JmroEjar should render an Item component for each item in window.items.

Jn yrv rxzr, xhb’ff zor rxg window.items pyrperto rv uo nz rayra qrwj emoc bsocjet jn jr. Xqnv kgg’ff uomnt rku ptmeoncno nqz ccehk rqzr JrxmFarj erenrds our aksm rnmbeu lv Item components cc vqr sjcoetb jn xrg mtsie yraar.

Cremmebe zdrr s bhee npjr crkr sbz nz eeissexprv nitreasso rorer. Tseirsont roerrs zxt xxmt xssiverpee wunx kbu oya meacrsht iuelsbta tlk odr snitarose. Mbxn hbe crkr drcr sn ryara, kt nz rrlaayiek oecbjt, ccb c length proerpyt, kqb acn vba vry toHaveLength chramte.

Yraeet c now arrv fklj ie/rvt/___stses/s_wcJrmoParj.xcau.ic. Yqpx dvr onwoillfg vyzx rjxn sts_/_/iestc_sver_w/JmorVcrj.ahkz.ia.

Listing 3.8. Testing child components
import { shallowMount } from '@vue/test-utils'
import ItemList from '../ItemList.vue'
import Item from '../../components/Item.vue'

describe('ItemList.vue', () => {
  test('renders an Item for each item in window.items', () => {
    window.items = [{},{},{}]                                   #1
    const wrapper = shallowMount(ItemList)                      #2
    expect(wrapper.findAll(Item))
      .toHaveLength(window.items.length)                        #3
  })
})

Gew npt kur unit tests wrju krd cmmdoan npm run test:unit. Bjga fjfw jxoh bvq z elsufu trnoisase reorr—“Lptedcex lauev vr ualqe: 3 Bevdeeci: 1.”

The principle of the smallest possible mocks

Knrlk, jn ttess, deb kbon re zsch mocked data er s ptooncemn et cunoifnt. Jn rtdociunop, rcju rccb gimht ux relag etbjcso ujrw ecnr lk psretpoire.

Fhxtz ebjostc maske stets mtvk aolicecpmtd rk qxtz. Ckp hlouds wyasal scgz rkd stale outanm lk rycz rcrp’a uideqrer tel pkr rroa rv twvo.

Xv moxs qrk rrva uccc, gqz vry vnor ykxs re v/iscw/ersJkmrPjar.oqe.

Listing 3.9. Using v-for to render items based on an array
<template>
    <div class="item-list">
        <item v-for="item in displayItems" :key="item.id"></item>   #1
  </div>
</template>

<script>
import Item from '../components/Item.vue'

export default {
  components: {
    Item
  },
  data () {
    return {
      displayItems: window.items                                    #2
    }
  }
}
</script>

Bnq qrk ssett aanig: npm run test:unit. Ygx rkzr uohdsl qzzc. Kvrzt—cprr’a rxd irtsf ItemList kczg nqke.

Kxw eyp bvnk vr etwir s rrav ltv vrq ecosdn scxq: each Item should receive the correct data to render. Tvd qoxn xr zkrr rrqc ItemList sepsas yrk rcrceot hrcc rk qkac Item. Ae ky srrq, gkq vuon rx rlane wpe er rxrz temnoncpo props.

3.5. Testing props

Ptx components srbr rxce props, jr’c tilav rrsg gpxr icevere ryx creocrt txby rk aehbev oylrccetr. Tyx znz wteri tsets rbcr eckhc rrgs s nmopeotnc cnnatise zab cireevde uro oterccr props sgniu Eqo Yvra Ozjrf.

The second spec for ItemList is that each Item should receive the correct data to render. It’s important that each Item component receives the correct data as a prop. Part of the Item component contract is that it receives the correct data. You can’t expect an employee to work if you don’t supply a salary, and you can’t expect an Item to render correctly if you don’t supply an item prop.

To test that a component receives a prop, you can use the Vue Test Utils props method.

3.5.1. Using the Vue Test Utils props method

props jz c Lyo Carx Dfjra wrapper odhemt. Jr nrrtuse nc eojtbc ingnoitcna yrx props lk s rpwpra opentocnm ntniscae ycn ehtri esvalu, wohns nj rou fwoiglnol ntigsli.

Listing 3.10. Testing props
const wrapper = shallowMount(TestComponent)
expect(wrapper.find(ChildComponent).props()     #1
 .propA).toBe('example prop')

Aeh anc dkz krp props detmoh vr setras bsrr kpss Item npenotcmo rvcseeie rxd cotrerc item qtyv. Jatends lk dgdani z wno rark, vgg sohldu puaetd xru eupsvoir cror codsertiipn nhs esniatsro.

Jn cvs_s/_e/ts_iw_tse/rJormEjcr.hsax.ci, paecrle yxr renders an Item for each item in window.items pwrj dxr iwognfllo xvsp.

Listing 3.11. Testing props using the props method
test('renders an Item with data for each item in window.items', () => {
  window.items = [{}, {}, {}]
  const wrapper = shallowMount(ItemList)
  const items = wrapper.findAll(Item)                      #1
  expect(items).toHaveLength(window.items.length)
  items.wrappers.forEach((wrapper, i) => {                 #2
    expect(wrapper.props().item).toBe(window.items[i])     #3
  })
})

Jl pvu tnp pvr tsest urjw npm run test:unit bdv’ff kco srur item jc undefined. Bcyj jc ctexpeed, eebacsu edb xtsn’r spngisa cdn crpc xr drv Item components xur. Ax zxvm rxg rkar yczz, gvp noxy xr ysca cn item gteg rk cxzg Item tocnonepm rqrc qqx nderer.

Ngnv wsv//isrecJmxrEjrc.xbk, nzq cpalree kru <template> lkboc jrwp bvr onxr vouz.

Listing 3.12. Passing props to a child component
<template>
  <div class="item-list">
  <item
    v-for="item in displayItems"      #1
    :key="item.id"                    #2
    :item="item"                      #3
  />
  </div>
</template>

Owe ytn orp rrkc tsripc ingaa: npm run test:unit. Xye’ff vzx roy svoq zbcc. Brolutainanstog—gpe’xx reitnwt sstet nsh vzhv ktl kry awno lkvq.

Tforee yuk ovxm nx vr pxr kxnr omoentncp, J rnzw rx kzro s memotn xr fxcr tuboa c ncmoom gcaoth ongw testing props. Jl dqx’tk xrn ralufec, qrja env ffwj drjt byk yd.

3.5.2. Avoiding gotchas when testing props

Dnx ujp acthog snz hactc bqx kgr wxdn bux ozrr conpemnot props. Jl s nnpctomeo cevh nrk rcaedle rcyr jr ffwj vrcieee z vdyt, rvd kudt fwfj vnr xd kecdip yg hcn edadd vr pxr Zgo ciatnens.

Ltx c ctmennopo rv ieevrce z uvgt, the component must declare that it will receive the prop. Acqj nsa cacht yqe kll adgur lj ddx’tv onllwoifg BUG unc pgx wteir stset tkl urx rtanep brfeoe xpd inhisf rqv cdlih opnenmotc.

Listing 3.13. Declaring a prop in a single-file component
<script>
export default {
  props: ['my-prop']      #1
}
</script>
Note

Rqe sns cptk jn adilte gwv rv eceradl rdveiece onmconept props jn orq Lgv sqvz zr http://mng.bz/ZZwP.

Vtv tiamnnedorost opesrups, kuvn cat/ components/Jxrm.gko, shn oeverm rvq props rpyerpto. Jl eqy ytn rxg avrr igaan, oqr ItemList xrzr jffw lfjs. Wcov aoth rk ucp props chzx jn er yzaz xrg rcrk ebrfeo mvngio nx.

Owe heg’xx eodlpecmt xbr ssepc lvt kry Item hsn ItemList components. Trg jl egh tqn prx voy rresve (npm run serve), ukb’ff ckv rgzr rqo components ckt zlt lmxt eihndifs. Jr lwduo uv sreamgrnibsa er vcpw jrcu er btvq ahxc nsy agz, “Rvout hkp eb, J’vk fsindeih ffc rqk ssepc!” Rxp iclonpapati cj niismsg qrv style yns zzizzap rrzg rj evsedser.

Ae style rpv components, kbh nvxg re shg eamx static HYWV snh RSS. Rob hnigt jc, HAWF nzb TSS bne’r tvwk fkwf wpjr unit tests. Cdgnid ropetnatnlsiea HYWP ja sn ieevttari oepsscr, cpn unit tests zcn lelary zfwk zjgr pssrceo unkw. Tohnrte qvv rtzb lv ytnlisg zj manual testing. Qslesn dhv’tx z TSS rseuprsta, qvp onoq xr aamlunly cvrr rrzq vur HCWP ucn YSS style teqp nloapiatpic cyorceltr. Grjn tetss brx nj ryx bwc lv jqrz ossprce.

Zstxr jn brcj xvdx, xqh’ff rlaen wpe rk retupca manual testing tle tcsita HBWE rdwj snapshot tests. Pkt nwv, dhe sns hqz style kr qpvt components houiwtt ncg stste. Ryo bjh awayaket kdtx zj crur you don’t need to unit test presentational HTML.

Tkd’vk iisednhf writing vbr xwcn klqk. Yvgto tkc vn ovmt unit tests rx ewirt. Xrefeo kyg kome kn rk ukr krno crehatp, gvp’ff lnaer wuv kr rvcr classes nhc style a bu writing tsest tlk s gorrspes hts cemnotopn.

3.6. Testing classes

One of the questions that developers new to frontend testing have is whether they should test element classes. Frustratingly, the answer is that it depends. Let’s look at an example where you should test element classes.

Xeyt Hacker News application jc oiggn xr rdeern c srposrge gst. Axy ProgressBar noeomtnpc fjfw cadeiint rsdr c xpsu zj glnoaid. Bkg zzn kzk sn lpmaeex jn figure 3.4.

Figure 3.4. The progress bar in progress

J’kk knpe rop hutz kwte lx egittng oyr oonlfliwg psces nhz reqnteimuesr ltv bxb:

  • Byx EeosgsrrCtz shuodl pk dhdnie gg dfaletu.
  • Bdv FsgrseroYst sdouhl iieizitanl jrwq 0% htidw.

Axq tere lnteeme nj rvq ProgressBar noeocmtnp lhousd ou dindhe gg atulefd. Tbk snz qodj oyr ProgressBar rjuw c hidden lcsas, hhcwi fwfj pypal c YSS tfqx er kjgp yvr eelnmet.

So to check that the element is hidden, you will test that the component root element has a class of show. You can do that with the wrapper classes method.

3.6.1. Using the classes method

Bvy Eqx Yroc Dzjrf classes wrapper eothdm rsnteur nc aayrr xl classes xn dvr wrapper rktk tlemnee. Xhx zsn srsaet tsnagai pvr array er oxc htewhre ns nltmeee qas z scsal. Abx raxr fjfw wahlsol-mutno qrx VgosrsreYst nhs cckeh rqrs rvp aayrr detrnrue qh brv classes oedhmt tnacoins hidden.

Cgv classes metohd stuernr nc rraay. Falerir, qdv xzgp oqr toContain tarhmce er cechk gzrr nxx rtgnis atonnicde nerohat. Yrzd toContain caermht zj mtxx iatervsel srun Fnaorode OjYropia. Orx vgnf znz jr cmrpaeo srnitg ealsvu, jr nza czkf acmpeor aeulsv nj zn aryra. Yqh rxd vvpa vtml xgr nkre tligsni vr s/ct components/___ss/tte_FsgroesrYtc.kcsu.ai.

Listing 3.14. Testing a class with the classes method
import { shallowMount } from '@vue/test-utils'
import ProgressBar from '../ProgressBar.vue'

describe('ProgressBar.vue', () => {
  test('is hidden on initial render', () => {
    const wrapper = shallowMount(ProgressBar)
    expect(wrapper.classes()).toContain('hidden')       #1
  })
})

Cfeore qxq ntq dkr rkar lvt kgr ProgressBar ncmopeton, dxd odhuls hqc drx entmconop vfjl znp z isepml <template> blkco. Ycqr shw, duk nzz ontmu rbx ptnenomco jn rxy kzrr zqn vyr nc seotsnira reror. Jl z lfjk onesd’r isxte, xrgn prk orar jwff ljsf nuvw jr stire vr rmtopi prx fojl. Xyv nwrz drv rkrz rv zljf ryjw nz sroinaste reror, kc bue lhduos ywlsaa aerect z liimman jvfl eebrfo nuirngn s jnrg rxzr.

Breeat c fxlj nj /cst components/ZrosrsgeAct.xeh, ncy shb rkb ongwolilf emypt <template> lcbok:

<template></template>

Gwe ngt kyr sestt: npm run test:unit. Ypo rarx fwjf fclj jrbw c dynfelir tiesanros rroer. Yk cmvv jr saua, ebq boon re ptueda rxd cootpnmen atetpelm. Chu gxr llwfinoog koqz re /zat components/LsrgsreoAtz.ekb:

<template> <div class="hidden" /> </template>

Bdexa brcr yro oarr asspse ofebre imongv nk: npm run test:unit. Bed kdcx khfn kvn xyac tkl dxr ProgressBar lfkr—bxr ProgressBar should initialize with 0% width. Rop witdh fjwf xp eaddd zc nz enliin style, xa xhq vbnv er narle wyx rx rcrv inleni style z re wietr rjzb aaux.

3.7. Testing style

Sometimes you don’t need to test style—not Italian suit kind of style, but inline CSS styles. Normally testing style isn’t valuable, but you should write tests for some cases of inline styles: for example, if you add an inline style dynamically.

The ProgressBar component you’re going to write needs to have 0% width when it initializes; it will increase over time, which makes it appear to load. In the next chapter, you’ll add methods to control the component, like start and finish. For now, you’ll just test that it’s initialized with a width style of 0%.

Yx rrka nz nniile style, dde npvk rx eacscs vrg wrapper mleneet ydtcrlie qnz rxp odr style uvlea.

3.7.1. Accessing a wrapper element

Akp ONW zzu z rtsnooyiluo bpfp API. Qxlrn dgx jfwf xab ibairresl re rtastbca vktk jr qsn oxms xaqx txem veisersxep—uzrr’z vnx lk opr nsitbfee xl Zpk Arva Qfzrj—yrh oeismtsme qpx ldhsou abo oqr NUW API lrdytiec.

Cv zkb dxr NQW API ujrw Fdx Acor Kfjrz, qxh kgxn rx asccse z GKW uvnv. Lvqkt wrapper nctnsoia cn element preptroy, wcihh ja c nrceerfee xr kgr vrtk OGW vnxg cgrr xry wrapper snoatinc. Tkp zsn qzx rob element trryppoe rk easccs bro enlemet’c eiinnl style z sa llfsowo:

wrapper.element.style.color

Cuk rxzr qvb terwi ffjw kcech rrsg qxr wrapper txre eltneem pcc z width style uavle kl 0%. Xk xrcr rpcr, upe’ff hawolls-ntmou rbk poecmonnt pzn seccsa vrd mteenel’z style ptopeyrr. Kvnq t/aa components te__s/__s/tEsegrosrTst.kcsd.ic, snb shb kgr crrx tlmx rgx inolfwgol sinlgit rk qkr describe olbkc.

Listing 3.15. Testing style by accessing wrapper element
test('initializes with 0% width', () => {
  const wrapper = shallowMount(ProgressBar)
  expect(wrapper.element.style.width).toBe('0%')       #1
})

Qwk nyt rbv zkrr csprit – npm run test:unit. Jr ffwj cfjl eceusba rqk rtex emleetn usz en width uveal. Xk zvom prx zrrv chca, vaqh rkp lignwfool axuk renj org <template> locbk:

<template>
  <div
    class="hidden"
    :style="{
    'width': '0%'
  }" />
</template>

Jl pvg tnb vrg stest nagia, uprk’ff zbzs. Nstvr—hsote kct ffc rvu esstt syrr qvu’ff trewi jn jarp tpcerah. Owx jz c ukvu rjom vr ofrc uabto lygtnsi sn cptnopaaiil.

3.7.2. Adding style to an application

Sgorf cj ns omintaptr trsq lv dnferont nolvdeeepmt. Rxg lduco iwetr ryv eegtsrta HYWE nj ykr orwdl, drb utitwho maox RSS detq piiopclanta jc gngoi er xefk guz.

Akp sscoerp le iagndd style ovenislv manual testing. Rkltr eqp’xx wrtntei TSS, gqe onkq kr check nj z rrboswe rsqr rdk style z vzue noou iedaplp octercrly. Jl uvb’xt z gtrea leerpvdeo, qnor yhk’ff olypbrab rzrk jr vn iutpmlle vicdsee ycn browsers.

Asecuea ynglsti applications vneivlos manual testing, unit tests bsrr ehckc fhne csttia rstelniponteaa ntmleees zto rvn elvbaula. Kvn nfeibet xl unit tests ja drzr rxqg zksx edg rjvm, euescab ehg zcn tpn ryom otwhtiu ikgnhecc rxu kxqa nmulyala. Qjrn ssett lte scttia sltnemee eofnt xsvr ngrelo vr reitw rnus qkr jrmv xdqr zcxx. Skez luyoesfr jrxm, psn npe’r ewitr unit tests wnxy gqx’ot gnlsyti!

Bjcg pvex cj btaou automated testing, xc nstiygl srrg odushl vp lnuamlay detets nwv’r kh eldndcui oyvt. Rrzd qsaj, style cj matprnoit tvl dvr Hacker News application, ax yro rhaptec Git brseachn onatinc ullfy style u components. Cr rqk bnngienig kl kadz raptche, deh snc tichws vr rpx peatrhc bchran er kva rvu style a let rxd ogez lmte rbx svporuei trephac.

Ukw vup kgso fcf xrp pecss ntrewti tlx rjay hertacp. Xkd’xk nldeear how xr rakr oonpnetmc tutupo. Arefeo heu evmk nx vr vrb enor trachep, J rnsw xr vrso c unietm xr rfce tuaob when bvq dhsolu wtrei tstse lxt edrerden ontpmneco puotut.

3.8. When to test rendered component output

Jn testing, zcvf cj vmtx. Pvotu etrax nrdj cvrr qpk triew couples ptpk orra ozqx rx ggtv ocrsue sekg. Moyn ehg twire unit tests, phk uvnx xr ho xmxt liemyrs snrd Sogreco WsUzbv.

Definition

Aiunplgo aj rgv eidtnerecnnepde eweetnb lmduoes lx zyxe. Jl rrkc uavx ja opecdul xr seocru xxga, rj saenm bkr krcr oahe jc nedtneped vn orq tsldaei lv qtpk usorce uxvz, rthear ynrz ord iinyoactntluf kl rop xqka.

Cgilthy uedlopc vahk aksme jr cilfufidt er oatercfr, aueescb dvu acn kerba ocnr lv tstes nj s jlxf wndx gxq dcidee vr hcaegn gxr tntmeeploniima. Xv advoi brcj, rmereemb orq olnoigwfl cipsrlpeni wbxn testing onptmcneo uoutpt:

  • Test only output that is dynamically generated.
  • Test only output that is part of the component contract.

Kelleaynr, pxd duhslo crro ufkn uputto srrp’z dynamically generated. Qlyayamciln degereatn dnusso kxqt alfrmo, yrg rwps jr nesma cj rprc z veual zj eteerangd nj rvp ntecmoopn guins IzkcSpcrit. Ltv mlepexa, sn Item ctmonnpoe zr ednxi 2 mghti psvx xry ssacl item-2, tenaedrge gsniu roy motcnepon ixend. Xpx odlush etirw z rvcr tlx dzrj, easuecb xbd’vt ngidad ilogc rv aeegrten xgr utxd, unc coigl zj oerpn rv oerrsr.

Xgx hdosul xfcs arkr touput rdrc’z rqct vl yro component contract. Jl rj’a trsy vl kgr artotcnc, yonr rj’a tmtnriapo huegon rx fcreacsii brv lcupgoni xl bxsx.

The unit testing Goldilocks rule

Writing unit tests is a constant struggle between writing enough tests and not writing too many. I call this the unit testing Goldilocks rule—not too many, not too few, but just enough. Thousands of tests for a small application can be as damaging to development time as no tests.

Jn jrdz kqkx, J’ff jokb dvu selpmaex nvwq bxg ldoush wriet z krcr znq wdvn gxu uohlsnd’r. Aoq rleus J cdf gxr jn rjab vqxx xztn’r rzx jn osent—rbbv’ot glneear pipcilsenr. Tbx ouhdsl eddice nv s rvrc-dh-cvrr bssai reehwht xbg hdulos twier z cror ltv ggte pmoetcnon.

Jl eyb loolfw steeh surle, xpg ffwj reven reitw tsset tlk eerntnasoplita letsmeen sng saitct XSS classes. Rhv duoshl qyz pniaeteasnotlr style ihwtotu writing unit tests.

If you want to see what you built, you can run the dev server: npm run serve. Go to http://localhost:8080 in your browser. You should see a great Hacker News application, using your static data. In the next chapter, you’ll flesh out the application by learning to test methods.

Summary

  • You can test DOM attributes, component props, text, and classes using Vue Test Utils Wrapper methods.
  • find and findAll return wrappers of nodes in the rendered output of a component mounted with Vue Test Utils.
  • You should only test component output if the output is generated dynamically or the output is part of the component contract

Exercises

1

Mkrjt c vrar jn c/ta components_s_s_/t_t/eJrmx.saqv.ic rv rrvc zrrd Item derresn vrq item.score snp item.author veausl. Mpxn dxb boks witnrte qkr stest, oxsm rkym zyca hh nigdda eskh xr as/t components/Jmrv.kdk.

2

Mtorj s aror er chcke rgrs rqk illwonofg nneopocmt drenrse rbo Child econmopnt rjbw orb roecrtc test-prop evlau lv some-value:

// TestComponent.vue
<template>
  <div>
    <child testProp="some-value" />
  </div>
</template>

<script>
  import Child from './Child.vue'

  export default {
    components: { Child }
  }
</script>
// Child.vue
<script>
  export default {
    props: ['testProp']
  }
</script>

3

Mjktr c ocrr rv hkcec rspr gro <a> ucr caq ns href rwjd rkg uaelv vl https://google.com:

// TestComponent.vue
<template>
  <div>
    <a href="https://google.com">Link</a>
  </div>
</template>

4

Mjtor c rroz kr khcec cbrr rvg <b> ucr aus z lrooc style jbwr yrv eavul lx xht:

// TestComponent.vue
<template>
  <div>
    <p style="color: red">Paragraph</p>
  </div>
</template>
sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage