Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
with QUnit
(PSOR\48QLWWRLQFUHDVH\RXUHIÀFLHQF\ZKHQWHVWLQJ
JavaScript code
Dmitry Sheiko
BIRMINGHAM - MUMBAI
Instant Testing with QUnit
&RS\ULJKW3DFNW3XEOLVKLQJ
$OOULJKWVUHVHUYHG1RSDUWRIWKLVERRNPD\EHUHSURGXFHGVWRUHGLQDUHWULHYDOV\VWHPRU
WUDQVPLWWHGLQDQ\IRUPRUE\DQ\PHDQVZLWKRXWWKHSULRUZULWWHQSHUPLVVLRQRIWKHSXEOLVKHU
H[FHSWLQWKHFDVHRIEULHITXRWDWLRQVHPEHGGHGLQFULWLFDODUWLFOHVRUUHYLHZV
(YHU\HIIRUWKDVEHHQPDGHLQWKHSUHSDUDWLRQRIWKLVERRNWRHQVXUHWKHDFFXUDF\RIWKH
LQIRUPDWLRQSUHVHQWHG+RZHYHUWKHLQIRUPDWLRQFRQWDLQHGLQWKLVERRNLVVROGZLWKRXW
ZDUUDQW\HLWKHUH[SUHVVRULPSOLHG1HLWKHUWKHDXWKRUQRU3DFNW3XEOLVKLQJDQGLWVGHDOHUV
DQGGLVWULEXWRUVZLOOEHKHOGOLDEOHIRUDQ\GDPDJHVFDXVHGRUDOOHJHGWREHFDXVHGGLUHFWO\RU
LQGLUHFWO\E\WKLVERRN
3DFNW3XEOLVKLQJKDVHQGHDYRUHGWRSURYLGHWUDGHPDUNLQIRUPDWLRQDERXWDOORIWKHFRPSDQLHV
DQGSURGXFWVPHQWLRQHGLQWKLVERRNE\WKHDSSURSULDWHXVHRIFDSLWDOV+RZHYHU3DFNW
3XEOLVKLQJFDQQRWJXDUDQWHHWKHDFFXUDF\RIWKLVLQIRUPDWLRQ
)LUVWSXEOLVKHG$XJXVW
3URGXFWLRQ5HIHUHQFH
3XEOLVKHGE\3DFNW3XEOLVKLQJ/WG
/LYHU\3ODFH
/LYHU\6WUHHW
%LUPLQJKDP%3%8.
,6%1
www.packtpub.com
Credits
Reviewer Proofreader
Sachin G. Kulkarni Stephen Copestake
Dmitry SheikoLVDZHEGHYHORSHUEORJJHUDQGRSHQVRXUFHFRQWULEXWRUOLYLQJDQGZRUNLQJ
LQWKHORYHO\FLW\RI)UDQNIXUWDP0DLQ*HUPDQ\
+HVWDUWHGOHDUQLQJFRPSXWHUSURJUDPPLQJLQWKHODWHHLJKWLHV6LQFHWKHODWHQLQHWLHVKHKDV
EHHQGHYHORSLQJZHEDSSOLFDWLRQVDQGWRROV&XUUHQWO\KHZRUNVDVDVHQLRUZHEGHYHORSHUIRU
WKHOHDGLQJJDPHGHYHORSHUFRPSDQ\&U\WHN*PE+
7KLVLV'PLWU\
VÀUVWERRNEXWKHKDVEHHQEORJJLQJIRUDGHFDGH<RXFDQÀQGKLVWKRXJKWV
DQGVROXWLRQVRQVXFKVXEMHFWVDVFRGHTXDOLW\VRIWZDUHDUFKLWHFWXUH-DYD6FULSW1RGHMV
7\SH6FULSW+70/$3,&66DQG3+3DWwww.dsheiko.com
'PLWU\
VYHU\ÀUVWRSHQVRXUFHFRQWULEXWLRQZDV%&:%DQ;6/7EDVHG&06LQ
6LQFHWKDWWLPHKHKDVEHHQFRQWULEXWLQJTXLWHDFWLYHO\+LVODWHVWZRUNVDUHDYDLODEOH
at https://github.com/dsheiko
,ZRXOGOLNHWRWKDQNP\EHDXWLIXOZLIH2OJDIRUKHUVXSSRUWDQGSDWLHQFH
WKURXJKRXWWKLVSURMHFW
About the Reviewer
Sachin G. KulkarniLVDIUHHODQFHUDQGFRQVXOWDQWZKRKHOSVLQEXLOGLQJVRIWZDUHIRU
EXVLQHVVHVWRDXWRPDWHEXVLQHVVDFWLYLWLHV+HKDVH[WHQVLYHO\ZRUNHGRQ3+30\6TODQG
+70/&66WREULQJRXWWKHEHVW8,GHVLJQDQGLPSOHPHQWEXVLQHVVQHHGV+LVODWHVW
SDVVLRQLVOHYHUDJLQJPRELOHGHYHORSPHQWXVLQJ-DYD6FULSWZLWK3+3EDFNHQGWRSURGXFH
IHDWXUHULFKDSSOLFDWLRQVIRUKLVFOLHQWV6DFKLQXVHVGHYHORSPHQWSDWWHUQVDQGEHVWSUDFWLFHV
WRHQMR\DSSOLFDWLRQGHYHORSPHQWWRLWVIXOOHVWH[WHQW)RUPDQ\\HDUVKHKDVEHHQLQYROYHG
LQRSHQVRXUFHSURMHFWVDQGJLYHVRSHQVRXUFHDGYLFHWRSHRSOH3OHDVHYLVLWKLVEORJDW
sachinkulkarni.infoWRNQRZPRUHDERXWKLP
www.PacktPub.com
6XSSRUWÀOHVH%RRNVGLVFRXQWRIIHUVDQGPRUH
<RXPLJKWZDQWWRYLVLWwww.PacktPub.comIRUVXSSRUWÀOHVDQGGRZQORDGVUHODWHGWR
\RXUERRN
'LG\RXNQRZWKDW3DFNWRIIHUVH%RRNYHUVLRQVRIHYHU\ERRNSXEOLVKHGZLWK3')DQGH3XE
ÀOHVDYDLODEOH"<RXFDQXSJUDGHWRWKHH%RRNYHUVLRQDWwww.PacktPub.com and as a print
ERRNFXVWRPHU\RXDUHHQWLWOHGWRDGLVFRXQWRQWKHH%RRNFRS\*HWLQWRXFKZLWKXVDW
service@packtpub.comIRUPRUHGHWDLOV
At www.PacktPub.com\RXFDQDOVRUHDGDFROOHFWLRQRIIUHHWHFKQLFDODUWLFOHVVLJQXS
IRUDUDQJHRIIUHHQHZVOHWWHUVDQGUHFHLYHH[FOXVLYHGLVFRXQWVDQGRIIHUVRQ3DFNWERRNV
DQGH%RRNV
TM
http://PacktLib.PacktPub.com
'R\RXQHHGLQVWDQWVROXWLRQVWR\RXU,7TXHVWLRQV"3DFNW/LELV3DFNW
VRQOLQHGLJLWDOERRN
OLEUDU\+HUH\RXFDQDFFHVVUHDGDQGVHDUFKDFURVV3DFNW
VHQWLUHOLEUDU\RIERRNV
Why Subscribe?
f )XOO\VHDUFKDEOHDFURVVHYHU\ERRNSXEOLVKHGE\3DFNW
f &RS\DQGSDVWHSULQWDQGERRNPDUNFRQWHQW
f 2QGHPDQGDQGDFFHVVLEOHYLDZHEEURZVHUV
)UHH$FFHVVIRU3DFNWDFFRXQWKROGHUV
,I\RXKDYHDQDFFRXQWZLWK3DFNWDWwww.PacktPub.com\RXFDQXVHWKLVWRDFFHVV
3DFNW/LEWRGD\DQGYLHZQLQHHQWLUHO\IUHHERRNV6LPSO\XVH\RXUORJLQFUHGHQWLDOVIRU
LPPHGLDWHDFFHVV
7DEOHRI&RQWHQWV
Preface 1
Instant Testing with QUnit 7
Setting up QUnit (Simple) 8
Testing assertions (Simple) 12
Writing a custom assertion plugin (Advanced) 19
Testing exceptions (Medium) 21
Testing asynchronous calls (Medium) 24
Organizing test cases (Simple) 28
Using a shared setup (Medium) 31
Testing user actions (Medium) 33
Running QUnit in the console (Advanced) 37
Cross-browser-distributed testing (Advanced) 40
Building a web project (Advanced) 43
QUnit and CI – setting up Jenkins (Advanced) 46
3UHIDFH
-DYD6FULSWZDVÀUVWUHOHDVHGDERXW\HDUVDJR,WKDGQRWEHHQWDNHQVHULRXVO\E\DZLGH
DXGLHQFHIRUPDQ\\HDUV7RGD\LWLVDPDWXUHDQGWKHPRVWSRSXODUSURJUDPPLQJODQJXDJH
:KDWHYHULVGRQHIRUWKH:HERQHZD\RUDQRWKHUUHOLHVRQ-DYD6FULSW)XUWKHUPRUHZLWKWKH
DGYHQWRI+70/UHODWHGWHFKQRORJLHVWKHGHYHORSPHQWIRFXVLVVKLIWLQJHYHQPRUHWRZDUGVD
ULFKLQWHUQHWDSSOLFDWLRQDUFKLWHFWXUH7KDWPHDQVIXUWKHUH[SDQVLRQRIVRSKLVWLFDWHGVFDODEOH
VROXWLRQVEDVHGRQ-DYD6FULSW7KHVHUYHUVLGH-DYD6FULSWLVUDSLGO\JDLQLQJLWVPRPHQWXP
WKHUHE\JLYLQJDQH[WHQVLYHGHPDQGIRUWKHDXWRPDWHGWHVWLQJRI-DYD6FULSW
48QLWLVDIUHDNLVKO\HDV\WRXVH-DYD6FULSWWHVWLQJIUDPHZRUNDWWKHVDPHWLPHLWLVTXLWH
SRZHUIXODQGÁH[LEOHHQRXJKWREHDJRRGFKRLFHIRUXQLWVDQGWKHIXQFWLRQDOWHVWLQJRIDQ\
VFDOHSURMHFWV
7KLVERRNSURYLGHVDVWHSE\VWHSJXLGHWRPDVWHULQJ48QLW,WVKRZVKRZWRVHWXSDQGXVH
48QLWKRZWRDXWRPDWHFURVVEURZVHUWHVWLQJZLWK48QLWDQGKRZWREHQHÀWIURP48QLWLQ
FRRSHUDWLRQZLWKFRQWLQRXVLQWHJUDWLRQWRROV
7KHJRDORIWKLVERRNLVWRKHOSWKHUHDGHUPDNHWKHPRVWRI48QLWLQDVKRUWDPRXQWRIWLPH
:KRWKLVERRNLVIRU
7KLVERRNZRXOGEHKDQG\IRUDQ\RQHZRUNLQJZLWK-DYD6FULSWZKRLVORRNLQJIRUDSRZHUIXO
EXWHDV\WRXVHWHVWLQJIUDPHZRUN
7KHUHDGHUGRHVQ
WQHHGWRNQRZDQ\SDUWLFXODUIUDPHZRUNEXWVKRXOGDWOHDVWNQRZWKHEDVLF
SULQFLSOHVRI-DYD6FULSWDQG+70/
7KHERRNZLOOEHPRVWSURÀWDEOHLIWKHUHDGHUKDVDQ\EDFNJURXQGLQDXWRPDWHGWHVWLQJ
2
Preface
Conventions
,QWKLVERRN\RXZLOOÀQGDQXPEHURIVW\OHVRIWH[WWKDWGLVWLQJXLVKEHWZHHQGLIIHUHQWNLQGVRI
LQIRUPDWLRQ+HUHDUHVRPHH[DPSOHVRIWKHVHVW\OHVDQGDQH[SODQDWLRQRIWKHLUPHDQLQJ
&RGHZRUGVLQWH[WDUHVKRZQDVIROORZV6HWXSWKHEXLOGVFULSWDVbuild.xmlLQWKHURRWRI
WKHSURMHFWZRUNLQJGLUHFWRU\
$EORFNRIFRGHLVVHWDVIROORZV
<?xml version="1.0"?>
<!DOCTYPE project>
<project name="tree" basedir="." default="build">
<target name="build" description="runs QUnit tests using PhantomJS">
<!-- Clean up output directory -->
<delete dir="./build/qunit/"/>
<mkdir dir="./build/qunit/"/>
<!-- QUnit Javascript Unit Tests -->
<echo message="Executing QUnit Javascript Unit Tests..."/>
</target>
</project>
:KHQZHZLVKWRGUDZ\RXUDWWHQWLRQWRDSDUWLFXODUSDUWRIDFRGHEORFNWKHUHOHYDQWOLQHVRU
LWHPVDUHVHWLQEROG
utils.subscribe( global, "load", function() {
var calc = document.getElementById("calc"),
num1 = document.getElementById("num1"),
num2 = document.getElementById("num2"),
sum = document.getElementById("sum");
3
Preface
$Q\FRPPDQGOLQHLQSXWRURXWSXWLVZULWWHQDVIROORZV
phantomjs runner.js test-suite.html
:DUQLQJVRULPSRUWDQWQRWHVDSSHDULQDER[OLNHWKLV
7LSVDQGWULFNVDSSHDUOLNHWKLV
5HDGHUIHHGEDFN
)HHGEDFNIURPRXUUHDGHUVLVDOZD\VZHOFRPH/HWXVNQRZZKDW\RXWKLQNDERXWWKLV
ERRN³ZKDW\RXOLNHGRUPD\KDYHGLVOLNHG5HDGHUIHHGEDFNLVLPSRUWDQWIRUXVWRGHYHORS
WLWOHVWKDW\RXUHDOO\JHWWKHPRVWRXWRI
7RVHQGXVJHQHUDOIHHGEDFNVLPSO\VHQGDQHPDLOWRfeedback@packtpub.comDQG
PHQWLRQWKHERRNWLWOHWKURXJKWKHVXEMHFWRI\RXUPHVVDJH
,IWKHUHLVDWRSLFWKDW\RXKDYHH[SHUWLVHLQDQG\RXDUHLQWHUHVWHGLQHLWKHUZULWLQJRU
FRQWULEXWLQJWRDERRNVHHRXUDXWKRUJXLGHRQwww.packtpub.com/authors
Customer support
1RZWKDW\RXDUHWKHSURXGRZQHURID3DFNWERRNZHKDYHDQXPEHURIWKLQJVWRKHOS\RX
WRJHWWKHPRVWIURP\RXUSXUFKDVH
'RZQORDGLQJWKHH[DPSOHFRGH
<RXFDQGRZQORDGWKHH[DPSOHFRGHÀOHVIRUDOO3DFNWERRNV\RXKDYHSXUFKDVHGIURP\RXU
account at http://www.packtpub.com,I\RXSXUFKDVHGWKLVERRNHOVHZKHUH\RXFDQ
visit http://www.packtpub.com/supportDQGUHJLVWHUWRKDYHWKHÀOHVHPDLOHGGLUHFWO\
WR\RX
4
Preface
Errata
$OWKRXJKZHKDYHWDNHQHYHU\FDUHWRHQVXUHWKHDFFXUDF\RIRXUFRQWHQWPLVWDNHVGR
KDSSHQ,I\RXÀQGDPLVWDNHLQRQHRIRXUERRNV³PD\EHDPLVWDNHLQWKHWH[WRUWKH
FRGH³ZHZRXOGEHJUDWHIXOLI\RXZRXOGUHSRUWWKLVWRXV%\GRLQJVR\RXFDQVDYHRWKHU
UHDGHUVIURPIUXVWUDWLRQDQGKHOSXVLPSURYHVXEVHTXHQWYHUVLRQVRIWKLVERRN,I\RXÀQG
DQ\HUUDWDSOHDVHUHSRUWWKHPE\YLVLWLQJhttp://www.packtpub.com/supportVHOHFWLQJ
\RXUERRNFOLFNLQJRQWKHerrata submission formOLQNDQGHQWHULQJWKHGHWDLOVRI\RXU
HUUDWD2QFH\RXUHUUDWDDUHYHULÀHG\RXUVXEPLVVLRQZLOOEHDFFHSWHGDQGWKHHUUDWDZLOOEH
XSORDGHGWRRXUZHEVLWHRUDGGHGWRDQ\OLVWRIH[LVWLQJHUUDWDXQGHUWKH(UUDWDVHFWLRQRI
WKDWWLWOH
Piracy
3LUDF\RIFRS\ULJKWPDWHULDORQWKH,QWHUQHWLVDQRQJRLQJSUREOHPDFURVVDOOPHGLD$W3DFNW
ZHWDNHWKHSURWHFWLRQRIRXUFRS\ULJKWDQGOLFHQVHVYHU\VHULRXVO\,I\RXFRPHDFURVVDQ\
LOOHJDOFRSLHVRIRXUZRUNVLQDQ\IRUPRQWKH,QWHUQHWSOHDVHSURYLGHXVZLWKWKHORFDWLRQ
DGGUHVVRUZHEVLWHQDPHLPPHGLDWHO\VRWKDWZHFDQSXUVXHDUHPHG\
3OHDVHFRQWDFWXVDWcopyright@packtpub.comZLWKDOLQNWRWKHVXVSHFWHGSLUDWHGPDWHULDO
:HDSSUHFLDWH\RXUKHOSLQSURWHFWLQJRXUDXWKRUVDQGRXUDELOLW\WREULQJ\RXYDOXDEOHFRQWHQW
Questions
<RXFDQFRQWDFWXVDWquestions@packtpub.comLI\RXDUHKDYLQJDSUREOHPZLWKDQ\
DVSHFWRIWKHERRNDQGZHZLOOGRRXUEHVWWRDGGUHVVLW
5
Instant Testing
with QUnit
:HOFRPHWR,QVWDQWWHVWLQJZLWK48QLW
)RUDORQJWLPH-DYD6FULSWZDVQ
WWDNHQVHULRXVO\+RZHYHUZLWKWKHDGYHQWRI$-$;
LWWXUQHGRXWWKDWXVLQJ-DYD6FULSWZHESDJHVFDQEHEURXJKWWRVSHHGZLWKWKHGHVNWRS
VRIWZDUH5,$5LFK,QWHUQHWDSSOLFDWLRQhttp://en.wikipedia.org/wiki/Rich_
Internet_application7KHIRUWKFRPLQJVHUYHUVLGH-DYD6FULSWSURYHGWKDWDSSOLFDWLRQV
FDQEHZULWWHQHQWLUHO\LQ-DYD6FULSW-DYD6FULSWEDVHGDSSOLFDWLRQVVWDUWHGJURZLQJXSDQG
JHWWLQJFRPSOH[(YHQLIJRRGGHYHORSHUVZRUNRQWKHPDQ\VRSKLVWLFDWHGVRIWZDUHZLOOVWLOO
KDYHGHIHFWV7HVWHQJLQHHUVGRWKHLUEHVWWRFDWFKWKHVHEHIRUHWKHSURGXFWLVUHOHDVHG
EXWLWLVKDUGO\SRVVLEOHWRGHWHFWDOOWKHÁDZVPDQXDOO\$XWRPDWHGWHVWLQJLVWKHEHVWZD\
WRLQFUHDVHWKHHIÀFLHQF\DQGFRYHUDJHRIVRIWZDUHWHVWLQJ%HVLGHVLWUHYHDOVDQ\SUREOHP
SUHVHQWLQWKHHDUO\GHYHORSPHQWVWDJHVZKLFKFXWVGRZQWKHFRVWRIÀ[LQJLWGUDVWLFDOO\
5HJDUGOHVVRIWKHWHFKQRORJLHVXVHGWKHEHVWGHYHORSPHQWSUDFWLFHVLQFOXGHDXWRPDWLRQ
RIXQLWWHVWVLQWHJUDWLRQWHVWVV\VWHPWHVWVDQGIXQFWLRQDOWHVWV8QLWWHVWVYHULI\LIWKH
VPDOOHVWIXQFWLRQDOSDUWVRIWKHDSSOLFDWLRQZRUNDVLQWHQGHGLQLVRODWLRQ,QWHJUDWLRQWHVWV
FKHFNLIWKHFRPSRQHQWVRIWKHSURMHFWFROODERUDWHSURSHUO\6\VWHPWHVWVH[DPLQHWKHZKROH
DSSOLFDWLRQ³XVXDOO\IRUFULWHULDVXFKDVXVDELOLW\SHUIRUPDQFHORDGVWUHVVVFDODELOLW\
FRPSDWLELOLW\DQGVHFXULW\³DQGIXQFWLRQDOWHVWVYDOLGDWHWKDWWKHDSSOLFDWLRQ8,LVÀQHIURP
WKHXVHU
VSHUVSHFWLYH
&XUUHQWO\WKHUHDUHSOHQW\RIWRROVPHDQWWRDFKLHYHDXWRPDWHGWHVWLQJLQ-DYD6FULSW
2QHRIWKHPRVWSRSXODUWHVWLQJIUDPHZRUNVLV48QLW,W
VVXIÀFLHQWO\SRZHUIXOH[WUHPHO\
ÁH[LEOHDQGDWWKHVDPHWLPHVXUSULVLQJO\HDV\WRVWDUWZLWK
Instant Testing with QUnit
7KLVLVDSUDFWLFDOERRNVKRZLQJKRZWRDFKLHYHDEHQHÀFLDODXWRPDWHGWHVWLQJRI\RXU
-DYD6FULSWDSSOLFDWLRQVZLWK48QLWDQGLWVSOXJLQVKRZWRHQJDJH48QLWIRUDXWRPDWHG
FURVVEURZVHUWHVWLQJDQGKRZWRXWLOL]H48QLWLQFRQMXQFWLRQZLWKGHYHORSHUDXWRPDWLRQ
DQGFRQWLQXRXVLQWHJUDWLRQWRROV
7KHERRNSURYLGHVIUDPHZRUNDJQRVWLF9DQLOOD-6H[DPSOHVRQDOOWKHFRQWULEXWHGWDVNV
WKDWRQHFDQÀQGHDV\WRIROORZ7KHVRXUFHFRGHLVFDWHJRUL]HGE\WKHWDVNVDQGDYDLODEOH
IRUGRZQORDGDWhttp://www.packtpub.com/support7KXVZKDWHYHU\RXUSUHYLRXV
H[SHULHQFHRUZKDWHYHUOLEUDULHV\RXZRUNZLWK\RXFDQPDVWHUWKHWDVNVLQQRWLPH
6HWWLQJXS48QLW6LPSOH
,QWKLVUHFLSHZHZLOOGRZQORDGWKH48QLWWHVWLQJIUDPHZRUNFRPSRQHQWVDQGFUHDWHDWHVW
UXQQHU+70/:HZLOOH[DPLQHKRZWKH48QLWWHVWUXQQHUUHSRUWVRQFDVHVRIERWKVXFFHHGHG
DQGIDLOHGWHVWV:HZLOODOVRFRQVLGHUVRPHRIWKHEHVWSUDFWLFHVRIWKHIUDPHZRUNÀOHVWUXFWXUH
Getting ready
7KHIUDPHZRUNFRQVLVWVRIWKH48QLW-DYD6FULSWPRGXOHWKHWHVWUXQQHU+70/DQGWKHVW\OH
VKHHW7HVWUXQQHULVDWHVWVXLWHH[HFXWHU,WORDGVRWKHU48QLWFRPSRQHQWVDQGJHQHUDWHVWKH
UHSRUWSDJH:HLQFOXGHRXUWHVWVHLWKHUGLUHFWO\LQWKHWHVWUXQQHU+70/RULQDQH[WHUQDOÀOH
7KHFRPSRQHQWPRGHOLVLOOXVWUDWHGDVIROORZV
Report qunit.js
tests.js
:HFDQVLPSO\FRS\SDVWHWKHWHVWUXQQHUFRGHIURPwww.qunitjs.comDVZHOODV
GRZQORDGLQJWKH&66DQG-DYD6FULSWFRPSRQHQWV,QIDFWLWFDQZRUNZLWKRXWGRZQORDGLQJE\
XVLQJWKHIROORZLQJOLQNVWRM4XHU\&'1LQWKHWHVWUXQQHU
f http://code.jquery.com/qunit/qunit-git.css
f http://code.jquery.com/qunit/qunit-git.js
8
Instant Testing with QUnit
How to do it
:HOOKRZGRHVLWZRUN"
<div id="sandbox">
<!--Put here sandbox HTML if needed -->
</div>
9
Instant Testing with QUnit
/RDG+70/LQ\RXUEURZVHU<RXZLOOVHHVRPHWKLQJVLPLODUWRWKH
IROORZLQJVFUHHQVKRW
6RZKDWLVRQWKHSDJH")LUVWLVWKHWLWOHWKDWZHDVVLJQHGLQWKH+70/8QGHUQHDWK
IROORZVDEDUWKDWLVLQFDOPLQJJUHHQLIDOOWKHWHVWVVXFFHHGHGDQGDOHUWLQJUHGLIDQ\
IDLOHG,QFDVHWKHWHVWVIDLOZHZLOOVHHVRPHWKLQJVLPLODUWRWKHIROORZLQJVFUHHQVKRW
)XUWKHUZHFDQVHHDWRROER[,WFRQWDLQVWKHIROORZLQJRSWLRQV
7KHHide passed testsFKHFNER[XVHIXOZKHQ\RXKDYHSOHQW\RIWHVWV
UXQQLQJDQGZDQWWRIRFXVRQO\RQWKRVHWKDWIDLOHG
7KHCheck for GlobalsFKHFNER[ZKLFKDOORZV\RXWRWHVWLIWKHJOREDOREMHFW
ZDVPRGLILHGGXULQJWHVWLQJ
10
Instant Testing with QUnit
7KHNo try-catchFKHFNER[ZKLFKWULJJHUVWKHUXQQLQJRIWKHWHVWVRXWVLGH
WKHWU\FDWFKEORFNUHYHDOLQJQDWLYHH[FHSWLRQV7KLVLVKDQG\ZKLOHGHEXJJLQJ
RQOHJDF\EURZVHUV
5LJKWEHORZWKHWRROER[48QLWGLVSOD\VWKHXVHUDJHQWGHWDLOV$IWHUWKDWZHKDYH
DUHSRUWDUHDZLWKWKHRYHUDOOVWDWLVWLFVDQGDOLVWRISHUWHVWUHVXOWV
How it works
<RXPD\OLNHWRNQRZZKDWUHDOO\KDSSHQVEHKLQGWKHVFHQHV:KLOHWKH48QLW-DYD6FULSW
PRGXOHLVEHLQJSURFHVVHGLWVXEVFULEHVDKDQGOHURQWKHwindow.onloadHYHQW
7KHKDQGOHULQLWLDOL]HVWKHQUnitREMHFWDQGSRSXODWHVWKHSODFHKROGHUVRIWKHWHVWUXQQHU
OD\RXWZLWKWKHHOHPHQWVUHTXLUHGWREXLOGWKHUHSRUWSDJH7RZDUGVWKHHQGLWFDOOVWKH
QUnit.startPHWKRGZKLFKDFWXDOO\UXQVWHVWVDYDLODEOHDWWKDWWLPH7KXVE\GHIDXOW
48QLWWULHVWRUXQWHVWVDVVRRQDVWKHSDJH'20LVUHDG\,I\RXZRUNZLWKPRGXODU-DYD6FULSW
VXFKDV$0'DQG&RPPRQ-6DOOWKHPRGXOHVLQFOXGLQJWHVWVDUHORDGHGDV\QFKURQRXVO\
7KHFDVHUHTXLUHVWKHWHVWUXQQHUWREHPDQXDOO\VWDUWHGZKLFKFDQEHSHUIRUPHGE\XVLQJ
DFRQÀJXUDWLRQRSWLRQDVGHVFULEHGLQWKHIROORZLQJFRGH
QUnit.config.autostart = false;
require(
[ "test-module1", "test-module2" ],
function() {
QUnit.start();
}
);
There's more
,QWKHSUHYLRXVH[DPSOHZHSODFHGWKHGXPP\WHVWFRGHVWUDLJKWLQWRWKHWHVWUXQQHU
+70/7KLVZRUNVÀQHIRUDVKRUWH[DPSOHEXWGRHVQ
WVXLWDUHDOSURMHFWZLWKDORWRIWHVWV
<RXZLOOKDYHWRFRPHXSZLWKDGHFHQWÀOHVWUXFWXUH,ZRXOGUHFRPPHQGFUHDWLQJDGHGLFDWHG
VXEGLUHFWRU\WHVWVLQ\RXUSURMHFW
Vwwwroot7KHUH\RXFDQDGGVXEGLUHFWRULHVSHUSURMHFW
PRGXOHXQGHUWHVW7KHVHVXEGLUHFWRULHVFRQWDLQWKHFRGHIRUPRGXOHWHVWVDQGWHVWUHODWHG
ÀOHVLIUHTXLUHG
8VXDOO\WHVWUXQQHULVVLPSO\QDPHGindex.htmlDVLWDJJUHJDWHVDOOWKH48QLWWHVWV
RIWKHSURMHFW,ZRXOGHQFDSVXODWHGLIIHUHQWNLQGVRIWHVWVLQWRUHVSHFWLYHUXQQHUVVXFK
as unit-tests.htmlfunctional-tests.htmlDQGacceptance-tests.html
11
Instant Testing with QUnit
7KXVZHFDQUXQIDVWXQLWWHVWVZLWKHYHU\FRPPLWIRUH[DPSOHE\D&RQWLQXRXV,QWHJUDWLRQ
VHUYHUDQGUXQIXQFWLRQDODQGDFFHSWDQFHWHVWVEHIRUHDQ\GHSOR\PHQW,WDOVRLPSURYHVWHVW
UHDGDELOLW\ZKHQ\RXNHHSIXQFWLRQDOWHVWVRXWRIDFFHSWDQFHWHVWV
app-wwwroot/
├── js/
│ └── moduleA.js
└── tests/
├── moduleA/
│ ├── dummies.js
│ ├── stubs.js
│ ├── mocks.js
│ ├── fixtures.js
│ └── unit-tests.js
└── unit-tests.html
7HVWLQJDVVHUWLRQV6LPSOH
$VVHUWLRQLVWKHPDLQPHWKRGXVHGLQWHVWLQJ,QWKLVUHFLSHZHZLOOH[DPLQHDOOWKHDVVHUWLRQ
PHWKRGVWKDW48QLWKDV%HVLGHVZHZLOOWDNHDORRNDWDIHZDVVHUWLRQPHWKRGVSURYLGHGE\
SRSXODU48QLWSOXJLQV
Getting ready
,WZRXOGEHPXFKPRUHXVHIXOLIZKLOHOHDUQLQJDVVHUWLRQVZHWHVWHGWKHUHDOFRGH
7KHIROORZLQJWZRKHOSHUIXQFWLRQVDUHTXLWHVXLWDEOHWRVKRZGLVFUHWHFDVHVRIWKHXVH
RIDVVHUWLRQV
var utils = (function() {
"use strict";
return {
/**
* Port of PHP trim function which differ from EcmaScript 5
String.prototype.trim
* Strip whitespace (or other characters) from the beginning and
end of a string
* Without the second parameter, trim() will strip these
characters:
* " " (ASCII 32 (0x20)), an ordinary space.
* «\t» (ASCII 9 (0x09)), a tab.
* "\n" (ASCII 10 (0x0A)), a new line (line feed).
* "\r" (ASCII 13 (0x0D)), a carriage return.
* "\0" (ASCII 0 (0x00)), the NUL-byte.
* "\x0B" (ASCII 11 (0x0B)), a vertical tab.
12
Instant Testing with QUnit
* @param {string} str
* @param {string} charlist
* @return {string}
*/
trim: function( str, charlist ) {
charlist = charlist || " \t\n\r\0\x0B";
return str.replace( new RegExp( "^[" + charlist + "]+|[" +
charlist + "]+$", "g" ), '' );
},
/**
* Emulate class-based inheritance.
* <example>
* ClassA = function() {}
* ClassB = utils.extendClass(ClassA, {
* prop: true
* });
* </example>
*/
extendClass: function( SuperType, subType ) {
var prop,
F = function(){};
F.prototype = new SuperType();
F.prototype.constructor = SuperType;
for ( prop in subType ) {
if ( subType.hasOwnProperty( prop ) ) {
F.prototype[ prop ] = subType[ prop ];
}
}
return F;
}
};
}( this ));
7KHÀUVWRQHLVDQDQDORJRIWKH3+3trimIXQFWLRQ,WUHPRYHVDQ\ZKLWHVSDFHVIURP
WKHEHJLQQLQJDQGHQGRIDVXSSOLHGVWULQJ7KHVHFRQGIXQFWLRQLPSOHPHQWVFODVVEDVHG
LQKHULWDQFH6R\RXFDQFUHDWHIRUWKLVIXQFWLRQDQHZFODVVconstructorE\H[WHQGLQJ
DJLYHQRQHSuperTypeZLWKDVSHFLÀHGREMHFWsubType
How to do it
'HÀQHWKHWHVWVFRSHZLWKWKHUHTXLUHGDVVHUWLRQVDVIROORZV
QUnit.test( "Test title", function( assert ) {
assert.<assertion-method>( comparison of actual and expected
values, "assertion title" );
});
13
Instant Testing with QUnit
+HUHWKHVFRSHRIWKHWHVWLVGHÀQHGWKDWFRPSULVHVRQHRUPRUHDVVHUWLRQV
48QLWSURYLGHVDVVHUWLRQPHWKRGVVXFKDVequalnotEqualstrictEqual
notStrictEqualokdeepEqualDQGnotDeepEqual
<RXPD\ILQGSOHQW\RIH[DPSOHVPDGHZLWKWKHVLPSOLILHGLQWHUIDFHRQ
WKH,QWHUQHW48QLWPHWKRGVDUHRIFRXUVHQDPHVSDFHGEXWWKH\DUH
DOVRH[SRVHGDVVKRUWFXWIXQFWLRQVRQWKHJOREDOREMHFW6R\RXFDQSXW
WKHFRGHLQWKHIROORZLQJZD\
test( "Test title", function() {
<assertion-method>( comparison of actual and
expected values, "assertion title" );
});
+RZHYHU,ZRXOGUDWKHUUHFRPPHQGDJDLQVWLW$V\RXVHHLW
VKDUGO\
VKRUWRIFRGHEXWLWPD\LQWHUIHUHZLWKWKHJOREDOSURSHUWLHVDGGHGE\
RWKHUVFULSWVDQG\RXZRQ
WEHDEOHWRDFFHVVSOXJLQDVVHUWLRQVWKLVZD\
14
Instant Testing with QUnit
([DPLQHWKHULVNVRIXVLQJZHDNHTXDOLW\DVVHUWLRQV
assert.notEqual( " \n\r\t ", false ); // fails
assert.notStrictEqual( " \n\r\t ", false ); // passes
});
7HVWWKHWZRLGHQWLFDOREMHFWVZLWKGHHSHTXDOLW\DVVHUWLRQV$WWKLVSRLQWZHKDYH
WULHGWKHHQWLUHFRUH48QLWDVVHUWLRQPHWKRGV+RZHYHU48QLW$3,DOVRLQFOXGHV
KHOSHUPHWKRGVdeepEqual and notDeepEqualWKDWDOORZ\RXWRDVVHUWRQWKH
HTXDOLW\RIFRPSOH[W\SHVVXFKDVDUUD\VREMHFWVUHJXODUH[SUHVVLRQVGDWHVDQG
IXQFWLRQV7KLVLVXVHIXOZKHQ\RXQHHGWRFRPSDUHWZRLGHQWLFDOREMHFWV(TXDOLW\
PHWKRGVequalstrictEqualZLOOQHYHUFRQÀUPWKHLGHQWLFDOREMHFWVXQOHVV
WKHVHDUHQRWUHIHUHQFHVWRWKHVDPHREMHFW2QWKHFRQWUDU\deepEqual recursively
FRPSDUHVPHPEHUVRIERWKWKHVXSSOLHGW\SHV7KHIROORZLQJFRGHLVDQH[DPSOHRI
WKHDVVHUWLRQWKDWWZRDUUD\VDUHLGHQWLFDO
QUnit.test( "Examine deepEqual", function( assert ) {
var dummy1 = {
propA: true,
propB: true
15
Instant Testing with QUnit
},
dummy2 = {
propA: true,
propB: true
};
There's more
%HVLGHV48QLWFRUHDVVHUWLRQPHWKRGV\RXFDQDOVRXVHWKHPHWKRGVSURYLGHGE\QXPHURXV
48QLWSOXJLQV:HZLOOH[DPLQHWKHPRVWSRSXODURQHVKHUH
'R\RXUHPHPEHUZHZHUHWHVWLQJWKHutils.trimIXQFWLRQ"7RPDNHVXUHWKHVWULQJLV
WULPPHGFRUUHFWO\LQPDQ\FDVHVZHZRXOGQHHGWRZULWHDORWRIDVVHUWLRQVUHSHDWLQJDOPRVW
WKHVDPHFRGHDOORYHUWKHWHVWVFRSH,WFDQEHVLPSOLÀHGQRWDEO\E\XVLQJWKH3DUDPHWHUL]H
SOXJLQ6HHWKHIROORZLQJH[DPSOH
QUnit
.cases([
{ str: ' string ', charlist: null, expected : 'string' },
{ str: ' string ', charlist: null, expected : 'string'
},
{ str: '\t\n\rstring ', charlist: null, expected :
'string' },
{ str: '||string|', charlist: "|", expected : 'string' },
])
.test("Test trim", function( params ) {
var expected = utils.trim( params.str, params.charlist );
strictEqual( expected, params.expected );
});
,I\RXQHHGWRDVVHUWWKDWWZRÁRDWQXPEHUVDUHHTXDOWRVRPHGHJUHHLWZRXOGEHQRWVRHDV\
WRDFKLHYHWKLVLQ-DYD6FULSW7KDWLVDQRWKHUEDGSDUWRIWKHODQJXDJH&RQVLGHUWKHIROORZLQJ
FRGHVQLSSHW
16
Instant Testing with QUnit
var a = 1.1234,
b = 1.1230;
2QWKHRWKHUKDQGWKHCloseSOXJLQhttps://github.com/JamesMGreene/qunit-
assert-closeSURYLGHVDPXFKPRUHFRQYHQLHQWZD\WRGRWKLVDVVKRZQLQWKH
IROORZLQJFRGH
assert.close(actual, expected, maxDifference, message);
assert.notClose(actual, expected, minDifference, message);
}( this ));
6RPHWLPHVZHKDYHWRPDNHVXUHWKDWIXQFWLRQVDUHEHLQJLQYRNHGLQDGHÀQHGRUGHU
IRUH[DPSOHWKH$0'FRPSDWLEOHPRGXOHORDGHUZRUNVDV\QFKURQRXVO\EXWPXVWUHVROYH
GHSHQGHQFLHVZKHQWKH\DUHVHTXHQFHG7KHStepSOXJLQhttps://github.com/
JamesMGreene/qunit-assert-stepDOORZVXVWRDVVHUWWKHSURSHUVHTXHQFHLQZKLFK
WKHFRGHVKRXOGSHUIRUP7KHIROORZLQJH[DPSOHYHULÀHVWKDWWKHbar function is called prior to
WKHfooIXQFWLRQ
(function(){
'use strict';
test('Test "foo" is invoked after "bar"', function( assert ) {
function foo() {
assert.step( 2 );
}
function bar() {
17
Instant Testing with QUnit
assert.step( 1, "bar is invoked first" );
}
bar();
foo();
});
}( this ));
1RZDGD\VWKHFDQYDVHOHPHQWLVZLGHO\XVHGWRUHQGHUGLDJUDPVVLPSOHDQLPDWLRQ
DQGEURZVHUEDVHGJDPHV2EYLRXVO\LWPHDQVDORWRIFRGHWHVWLQJ+RZHYHUDQ\DVVHUWLRQ
RQDFDQYDVUHJLRQGRHVQ
WVHHPDQHDV\WDVN7KHCanvasSOXJLQhttps://github.com/
JamesMGreene/qunit-assert-canvasSURYLGHVWKHDVVHUWLRQPHWKRGWRWHVWLQGLYLGXDO
SL[HOYDOXHVLQDFDQYDV,QWKHIROORZLQJH[DPSOHZHGHÀQHDFDQYDVDUHDRQWKHSDJHDQG
GUDZDEODFNUHFWDQJOH7KHQZHDVVHUWDGRWRQWKHUHFWDQJOHWKDWKDVEODFNFRORU
<div id="sandbox"><canvas width="5" height="5"></canvas></div>
<script type="text/javascript" src="http://code.jquery.com/qunit/
qunit-git.js"></script>
<script type="text/javascript" src="../vendors/Canvas/qunit-assert-
canvas.js"></script>
<script type="text/javascript">
(function( window ){
'use strict';
var document = window.document,
canvas = document.getElementById('sandbox').firstChild,
context = canvas.getContext('2d');
}( this ));
</script>
+70/WROHUDWHV+70/OLNHORRVHQHVVVXFKDVQRQFORVHGHOHPHQWVDQGQRQHVFDSHG
DWWULEXWHYDOXHV7KLVPDNHVLWGLIÀFXOWWRFRPSDUHWZRGLIIHUHQW+70/VWULQJVWKDWDUHWUHDWHG
HTXDOO\E\DEURZVHU7KH+70/SOXJLQhttps://github.com/JamesMGreene/qunit-
assert-htmlVROYHVWKHLVVXHLQWKHIROORZLQJZD\
(function( global ){
'use strict';
18
Instant Testing with QUnit
assert.htmlEqual('<B TITLE=test>test</B>', '<b
title="test">test</b>');
});
}( this ));
Getting ready
48QLWLVHDV\WRVWDUWZLWKDVLWSURYLGHVRQO\DIHZQHFHVVDU\DVVHUWLRQPHWKRGV
1HYHUWKHOHVV\RXFDQKDYHDVPDQ\PHWKRGVDV\RXZLVKE\XVLQJ48QLWSOXJLQV3OHQW\RI
SOXJLQVDUHDOUHDG\DYDLODEOHRQWKH,QWHUQHW%HVLGHV\RXPD\QHHG\RXURZQFXVWRPSOXJLQ
/XFNLO\48QLWKDVD&DOOEDFN$3,ZKLFKPDNHVLWHDV\<RXFDQVLPSO\UHJLVWHU\RXURZQ
KDQGOHURQWKHHQWU\DQGH[LWSRLQWVRIHYHU\WHVWLQJVFRSHWHVWVXLWHPRGXOHDQGWHVWRU
VXEVFULEHDKDQGOHUIXQFWLRQWRWKHHYHQWWKDWKDSSHQVHYHU\WLPHDVVHUWLRQSHUIRUPHGLQD
WHVWIRULQVWDQFH3KDQWRP-6www.phantomjs.orgLVZLGHO\XVHGWRUXQ48QLWWHVWVLQWKH
FRQVROH+RZHYHUWKH48QLWRXWSXWWHVWUHVXOWVDVDQ+70/UHSRUWLQ'20ZKLFKGRHVQ
WZRUN
ZLWKWKHFRPPDQGOLQH7KH3KDQWRP-6UXQQHU-DYD6FULSW1RGH-6XWLOLW\VROYHVWKLVLVVXH
E\WUDQVODWLQJWKH48QLWRXWSXWLQWRDIRUPDWFRPSDWLEOHZLWKWKHFRPPDQGOLQHLQWHUIDFH
,WUHJLVWHUVDFDOOEDFNZLWKWKHQUnit.logPHWKRGWRFROOHFWDQGVWRUHUHVXOWVRQHYHU\
DVVHUWLRQ,WDOVRVXEVFULEHVDKDQGOHURQWKHWHVWVXLWHFRPSOHWHHYHQWZLWKQUnit.done
ZKLFKRXWSXWVWKHDFFXPXODWHGUHVXOWVLQWKHUHTXLUHGZD\
+RZHYHULI\RXZULWHMXVWDQDVVHUWLRQPHWKRG\RXPD\GRZLWKRXWWKHFDOOEDFNV$3,LQPRVW
FDVHV<RXFDQVLPSO\DGG\RXUPHWKRGLQWKHQUnit.assertREMHFW
How to do it
:ULWHDQDVVHUWLRQPHWKRG,PDJLQHWKDWZHQHHGDFRQYHQLHQWPHWKRGIRUDVVHUWLQJ
WKDWDVXSSOLHGHOHPHQWLVYLVLEOH7KHPHWKRGLVYHU\VLPSOH³LWRQO\FKHFNVLIWKH
HOHPHQWLVKLGGHQYLDVW\OHSURSHUWLHVYLVLELOLW\DQGGLVSOD\
function isVisible( node ) {
return node.style.visibility !== "hidden" &&
node.style.display !== "none";
}
19
Instant Testing with QUnit
6DYHWKLVFRGHLQWKHqunit-assert-is-visible.jsÀOH
3URYLGHWKHWHVWUXQQHU+70/DVIROORZV
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Plugin Usage Example</title>
<link rel="stylesheet" href="http://code.jquery.com/qunit/
qunit-git.css" type="text/css" media="screen">
</head>
<body>
<h1 id="qunit-header">Plugin Usage Example</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup</div>
<div id="sandbox">
<div id="myTarget"><!-- --></div>
</div>
20
Instant Testing with QUnit
<script type="text/javascript" src="http://code.jquery.com/
qunit/qunit-git.js"></script>
<script type="text/javascript" src="../js/qunit-assert-is-
visible.js"></script>
<script type="text/javascript">
"strict mode";
QUnit.test( "Test myTarget is visible", function( assert )
{
var node = document.getElementById("myTarget");
assert.isVisible( node );
});
</script>
</body>
</html>
/RDGWKHWHVWUXQQHULQDEURZVHUDQGH[DPLQHWKHUHVXOWVVKRZQLQWKH
IROORZLQJVFUHHQVKRW
7HVWLQJH[FHSWLRQV0HGLXP
,QWKLVUHFLSHZHZLOOOHDUQKRZWRKDQGOHWKHIXQFWLRQ
VH[FHSWLRQDOEHKDYLRUKRZWRGHÀQH
FXVWRPH[FHSWLRQVDQGKRZWRWHVWZKHWKHUH[FHSWLRQVDUHUHDOO\WKURZQIRULQYDOLGLQSXWDQG
WKHVHDUHH[FHSWLRQVRIWKHLQWHQGHGW\SHV
Getting ready
:H
YHOHDUQWDOUHDG\KRZWRWHVWZKHWKHURUQRWDIXQFWLRQSHUIRUPVWKHLQWHQGHGEXVLQHVV
ORJLF%XWZKDWDERXWLWVH[FHSWLRQDOEHKDYLRU"/HW
VVD\ZHVXSSO\LQYDOLGLQSXWSDUDPHWHUV
:HH[SHFWWKHIXQFWLRQWRZDUQXVDERXWWKHP7KHEHVWSUDFWLFHZRXOGEHWRFKHFNZKHWKHU
WKHSDUDPHWHUVKDYHEHHQSDVVHGRQWRWKHIXQFWLRQHQWU\SRLQWDQGWKURZQH[FHSWLRQVLIDQ\
RIWKHSDUDPHWHUVDUHLQYDOLG:HQHHGDZD\WRWHVWWKLVORJLFDVZHOO
21
Instant Testing with QUnit
'R\RXUHPHPEHUWKHutils.trimIXQFWLRQH[DPSOH":HKDYHWRPRGLI\LWQRZIRUFKHFNLQJ
WKHYDOLGLW\RIWKHLQSXWSDUDPHWHUV
var trim = function( str, charlist ) {
:HOOWKHIXQFWLRQWKURZVH[FHSWLRQZKHQHYHULWGHWHFWVDQLQYDOLGLQSXWEXWWKHVHDUHFXVWRP
H[FHSWLRQVWKDWPXVWEHGHÀQHG7KLVFDQEHGRQHE\GHFODULQJQHZHUURUREMHFWFRQVWUXFWRUV
WKDWLQKHULWIURPVWDQGDUG-DYD6FULSWHUURUV3OHDVHÀQGWKHLPSOHPHQWDWLRQIRUWKLVLQWKH
IROORZLQJH[DPSOH
"strict mode";
var utils = (function( global ) {
"use strict";
return {
/**
* Port of PHP trim function
* @param {string} str
* @param {string} charlist
* @return {string}
*/
trim: function( str, charlist ) {
// function body from the example above
},
/**
* @constructor
*/
InvalidReferenceException: function( message ) {
this.name = "InvalidReferenceException";
22
Instant Testing with QUnit
this.message = message || "InvalidReferenceException thrown";
},
/**
* @constructor
*/
InvalidTypeException: function( message ) {
this.name = "InvalidTypeException";
this.message = message || "InvalidTypeException thrown";
}
};
}());
How to do it
'HÀQHWKHWHVWVFRSHDQGWKHDVVHUWH[SHFWHGH[FHSWLRQLVWKURZQ,QWKLVFDVH48QLW
SURYLGHVWKHthrowsPHWKRGZKLFKFDQEHGHVFULEHGZLWKWKHIROORZLQJFRGH
QUnit.test( "Test title", function( assert ) {
assert.throws( "callback throwing exception, expected
exception", "assertion title" );
});
7HVWZKHWKHUutils.trimYDOLGDWHVWKHLQSXWSDUDPHWHUVDQGWKURZVWKH
LQWHQGHGH[FHSWLRQV
QUnit.test( "Test utils.trim contract violation", function( assert
){
assert.throws( function() {
utils.trim("");
}, utils.InvalidReferenceException,
"str agrument must not be empty"
);
assert.throws( function() {
utils.trim( 1 );
}, utils.InvalidTypeException,
"str agrument must be a string"
);
23
Instant Testing with QUnit
assert.throws( function() {
utils.trim( "string", 1 );
}, utils.InvalidTypeException,
"charlist agrument must be a string"
);
});
/RDGWKHWHVWUXQQHULQDEURZVHUDQGH[DPLQHWKHUHVXOWVVKRZQLQWKH
IROORZLQJVFUHHQVKRW
7HVWLQJDV\QFKURQRXVFDOOV0HGLXP
,QWKLVUHFLSHZHZLOOVHHKRZ48QLWGHDOVZLWKDV\QFKURQRXVFDOOEDFNV:HZLOOZULWHDQ
DVVHUWLRQWHVWIRUDQDV\QFKURQRXVO\FDOOHGIXQFWLRQ:HZLOODOVRJRWKURXJKDQH[DPSOHRI
WHVWLQJWKH;+5DVVRFLDWHGIXQFWLRQE\PRFNLQJWKHWUDQVIHUPHWKRG
Getting ready
2QHRIWKHJUHDWHVWIHDWXUHVRI-DYD6FULSWLVLWVDELOLW\WRFDOOIXQFWLRQVDV\QFKURQRXVO\
<RXFDQHDVLO\PDNHPXOWLSOHIXQFWLRQVWKDWH[HFXWHVLPXOWDQHRXVO\DVIROORZV
window.setTimeout( function() {
yourFunc1( param1, paramN );
}, 0);
24
Instant Testing with QUnit
$V\RXXQGHUVWDQGWKHVHIXQFWLRQVFDQQRWEHWHVWHGWKHZD\ZHGLGEHIRUH:HFDQQRWDVVHUW
RQWKHIXQFWLRQVWUDLJKWDIWHULWLVODXQFKHG2WKHUZLVHZHULVNDVVHUWLQJRQWKHFDOOEHIRUHLWLV
FRPSOHWHG:HZLOOHQFRXQWHUWKHVDPHSUREOHPZKLOHWHVWLQJKDQGOHUVVXEVFULEHGWRHYHQWV
DQGHYHQZKLOHXVLQJ-DYD6FULSWREMHFWVDVVRFLDWHGZLWKHYHQWV³IRULQVWDQFH;+5
How to do it
'HÀQHWKHWHVWVFRSH)RUWHVWLQJDV\QFKURQRXVO\FDOOHGIXQFWLRQV48QLWSURYLGHV
WKHasyncTestPHWKRGZKLFKLQFRQWUDVWWRWKHWHVWPHWKRGUHFHLYHVDQDGGLWLRQDO
SDUDPHWHUZLWKDQXPEHURIH[SHFWHGDVVHUWLRQV7KXV48QLWZLOOVHWWKHWHVWDV
FRPSOHWHRQO\ZKHQWKHVXSSOLHGQXPEHURIDVVHUWLRQVLVSHUIRUPHG
/**
* Test async. called function
* @param {string} test title
* @param {number} number of expected assertions
* @param {function) test scope
*/
QUnit.asyncTest( "Test title", 1, function( assert ){
});
$VVHUWZLWKLQWKHDV\QFKURQRXVFDOOEDFN6LQFHZHDVVHUWDV\QFKURQRXVO\ZHKDYH
WRFDOOWKHQUnit.startPHWKRGDVVRRQDVWKHODVWDVVHUWLRQLVGRQH7KLVZLOOOHW
48QLWNQRZZKHQLWFDQMXPSWRWKHQH[WWHVW
/**
* Test async. called function
* @param {string} test title
* @param {number} number of expected assertions
* @param {function) test scope
*/
QUnit.asyncTest( "Test async control", 1, function( assert ){
window.setTimeout( function() {
assert.ok( true, "captured" );
QUnit.start();
}, 0);
});
25
Instant Testing with QUnit
/RDGWKHWHVWUXQQHULQDEURZVHUDQGH[DPLQHWKHUHVXOWVVKRZQLQWKH
IROORZLQJVFUHHQVKRW
There's more
7KHGHVFULEHGDSSURDFKZRUNVÀQHZLWKWKHWHVWIULHQGO\FRGH,PHDQWKHFRGHZKHUH
WKHDV\QFKURQRXVFDOODFFHSWVWHVWFDOOEDFNLQMHFWLRQRUÀUHVDQHYHQWRQZKLFKDKDQGOHU
LVVXEVFULEHG:KHQ\RXDUHZRUNLQJZLWKEODFNER[FRGH\RXKDYHQRZD\WRDVVHUWRQ
DV\QFKURQRXVFDOOVH[FHSWE\PRFNLQJWKHWUDQVIHUXWLOLW\ZKRVHLQWHUIDFH\RXDUHIDPLOLDU
ZLWKIRUH[DPSOHZKLOHXQLWWHVWLQJDIXQFWLRQWKDWUHOLHVRQ;+5ZHKDYHWRPRFNWKHKHOSHU
SHUIRUPLQJWKHUHTXHVW:HGRLWDQ\ZD\DVLWLQFOXGHVDQDFURVVQHWZRUNFRPPXQLFDWLRQDQG
JHWVRXWRIWKHXQLWVFRSH/HW
VVD\WKHIXQFWLRQXQGHUWHVWXVHVWKHM4XHU\JHWPHWKRG
:HNQRZLWVLQWHUIDFHDQGFDQRYHUULGHLWZLWKRXURZQPHWKRGZKLFKGRHVQRWKLQJEXW
UHSRUWWR48QLW<RXFDQVHHWKLVLQWKHIROORZLQJH[DPSOH
var
/**
* Object representing an abstract RSS Feed
* @constructor
*/
RssFeed = function() {
var xml;
return {
/**
* Load data from a remote source
* @param {string} url
*/
26
Instant Testing with QUnit
loadData: function( url ) {
$.get( url, function( rsp ){
xml = rsp;
});
},
/**
* Get last loaded data
*/
getData: function() {
return xml;
}
};
};
/**
* Mock XHR helper just for the case
* @param {string} url
* @param {function} callback
*/
$.get = function( url, callback ) {
window.setTimeout( function(){
callback("data");
assert.ok( true, "data requested" );
QUnit.start();
}, 0 );
};
feed.loadData("http://google.com");
});
7KHRSSFeedFRQVWUXFWRUSURGXFHVDQREMHFWWKDWFDQORDGGDWDIURPDUHPRWHVRXUFH'DWD
LVEHLQJORDGHGDV\QFKURQRXVO\XVLQJ$.get7RWHVWWKHPHWKRGZHRYHUULGH$.getZLWKRXU
RZQPHWKRG2XULPSOHPHQWDWLRQLQYRNHVDVXSSOLHGFDOOEDFNDVH[SHFWHGIURPjQuery.get
DQGGRHVLWDV\QFKURQRXVO\E\XVLQJsetTimeout7RZDUGVWKHHQGLWPDNHVWKHDVVHUWLRQ
DQGQRWLÀHV48QLWWKDWWKHWHVWLVFRPSOHWH
27
Instant Testing with QUnit
2UJDQL]LQJWHVWFDVHV6LPSOH
,QWKLVUHFLSHZHZLOOOHDUQKRZWRNHHSWKHWHVWVORJLFDOO\RUJDQL]HGE\XVLQJWKHQUnit.
modulePHWKRG
Getting ready
:KLOHZRUNLQJRQDUHDOSURMHFWWKHQXPEHURIWHVWVFDQEHSUHWW\KXJHIRUH[DPSOH
WKHM4XHU\WHVWVXLWHFRQWDLQVDERXWWHVWVVKRZQLQWKHIROORZLQJVFUHHQVKRW
1DYLJDWLRQRQVXFKOLVWVZLWKRXWDQ\JURXSLQJDELOLWLHVZRXOGEHDWRXJKWDVN/XFNLO\48QLW
SURYLGHVWKHPRGXOHPHWKRGZKLFKDOORZVXVWRRUJDQL]HWKHWHVWVDQGUXQDVSHFLÀFJURXSRI
WHVWVLQGLYLGXDOO\
28
Instant Testing with QUnit
,PDJLQHZHKDYHDQDSSOLFDWLRQWKDWKDQGOHVXVHULQWHUDFWLRQZLWKWKH6LJQ,QDQG3DVVZRUG
5HVHWIRUPV:HGRQ
WQHHGDQ\LPSOHPHQWDWLRQGHWDLOVIRUQRZEXWUDWKHUDKLJKOHYHO
FRQFHSWDVIROORZV
var ResetPasswordForm = function() {
return {
/** Render and synchronize UI */
init: function() {
},
/** Show panel */
show: function() {},
/** Hide panel */
hide: function() {},
/** Validate form input */
validateInput: function() {}
};
},
SignInForm = function() {
return {
/** Render and synchronize UI */
init: function() {
},
/** Show panel */
show: function() {},
/** Hide panel */
hide: function() {},
/** Validate form input */
validateInput: function() {}
};
}
7KXVZHKDYHWZRREMHFWFRQVWUXFWRUVDQGWKUHHPHWKRGVHDFKWKDWPXVWEHWHVWHG/HW
VVSOLW
XSWKHWHVWLQWRWZRJURXSVRQHIRUWKH6LJQ,QIRUPDQGRQHIRU5HVW3DVVZRUG)RUP
How to do it
'HÀQHDWHVWJURXSmoduleIRUWKHSignInFormFRQVWUXFWRU
QUnit.module("SignInForm");
3XWXQGHUQHDWKDOOWKHWHVWVUHOHYDQWWRWKHJURXS
QUnit.test( "show method", function( assert ){
// Stub assertion
assert.ok( true );
});
29
Instant Testing with QUnit
QUnit.test( "hide method", function( assert ){
// Stub assertion
assert.ok( true );
});
QUnit.test( "validateInput method", function( assert ){
// Stub assertion
assert.ok( true );
});
Repeat the flow for the ResetPasswordForm constructor.
// Define the group
QUnit.module("ResetPasswordForm");
QUnit.test( "show method", function( assert ){
// Stub assertion
assert.ok( true );
});
QUnit.test( "hide method", function( assert ){
// Stub assertion
assert.ok( true );
});
QUnit.test( "validateInput method", function( assert ){
// Stub assertion
assert.ok( true );
});
/RDGWKHWHVWUXQQHULQDEURZVHUDQGH[DPLQHWKHUHVXOWVVKRZQLQWKH
IROORZLQJVFUHHQVKRW
30
Instant Testing with QUnit
8VLQJDVKDUHGVHWXS0HGLXP
,QWKLVUHFLSHZHZLOOJXLGH\RXRQWHVWJURXSRSHUDWLRQVVXFKDVVKDUHGSUHOLPLQDU\WDVNVDQG
KRZWRNHHSWKHWHVWLQJHQYLURQPHQWFOHDQEHWZHHQWHVWH[HFXWLRQV
Getting ready
:KLOHZULWLQJPRUHDQGPRUHWHVWV\RXZLOOUHDOL]HWKDWWRJHWFOHDUUHVXOWV\RXQHHGWR
UHSHDWHGO\VHWXSWKHHQYLURQPHQWLQDVSHFLÀFVWDWHEHIRUHUXQQLQJDWHVWDQGUHVWRUHLWV
RULJLQDOVWDWHDIWHUZDUGV7KLVLVDWLPHFRQVXPLQJWDVNWKDWLQYROYHVFRGHGXSOLFDWLRQ
0RVWWHVWLQJIUDPHZRUNVLQFOXGLQJ48QLWVXSSRUWWKHVRFDOOHGVKDUHGVHWXSFRGH7KXV
\RXFDQGHÀQHWZRFDOOEDFNVIRUDJURXSRIWHVWV7KHÀUVWRQHsetupWDNHVUHVSRQVLELOLW\
IRUUHFUHDWLQJWKHLQWHQGHGHQYLURQPHQWVWDWHSULRUWRHYHU\WHVWRIWKHH[HUFLVLQJJURXS
7KHVHFRQGteardownFOHDQVXSWKHFKDQJHVPDGHWRWKHHQYLURQPHQWDIWHUHYHU\WHVW
LVGRQH
7RFRQVLGHUWKHDGYDQWDJHVRIWKHDSSURDFKOHW
VWDNHDVDPSOHPRGXOHIRUWHVWLQJ
/**
* @constructor
* @returns {object}
*/
var SignInForm = function( boundingBox ) {
return {
/** Show panel */
show: function() {
boundingBox.style.display = "inline-block";
},
/** Hide panel */
hide: function() {
boundingBox.style.display = "none";
}
};
};
$FRQVWUXFWRUSURGXFLQJSignInFormFDQKLGHRUVKRZXS'XULQJLQLWLDOL]DWLRQWKHREMHFW
SignInFormELQGVWRWKHHOHPHQWV$QLQVWDQFHRISignInForm H[SHFWVRI+70/OD\RXW
DQHOHPHQWWRELQGWR7KHSignInFormPHWKRGVDUHSHUPLWWHGWRPRGLI\'20ZLWKLQWKH
ERXQGLQJER[HOHPHQW7KHRUHWLFDOO\HYHU\VHFRQGWHVWVWDUWVRQDGLYHUVH'20WKDWPD\
DIIHFWWHVWUHVXOWV:HFDQSUHYHQWWKHSUREOHPE\DGGLQJDFOHDQERXQGLQJER[WRWKH'20
WUHHRQWKHWHVWVHWXSDQGUHPRYLQJLWZLWKWHDUGRZQ
31
Instant Testing with QUnit
How to do it
'HÀQHDWHVWJURXSmoduleIRUWKHSignInFormFRQVWUXFWRUDVIROORZV
QUnit.module("SignInForm");
7KHVHFRQGSDUDPHWHUQUnit.moduleRIWKHPRGXOHPHWKRGH[SHFWVDQREMHFW
FDOOHGVKDUHGVHWXSFRQÀJXUDWLRQ
QUnit.module("SignInForm", {
setup: function() {
},
teardown: function() {
}
});
$GGWKHFRGHDSSHQGLQJWKHERXQGLQJER[HOHPHQWWRWKH'20WUHHRQVHWXSDQGWKH
FRGHUHPRYLQJLWRQWHDUGRZQDVIROORZV
QUnit.module("SignInForm", {
// set up testing environment
setup: function() {
this.fixture = document.createElement("div");
document.getElementsByTagName('body')[ 0 ].appendChild(
this.fixture );
},
// restore the initial state
teardown: function() {
document.getElementsByTagName('body')[ 0 ].removeChild(
this.fixture );
}
});
0RYHWKHSignInFormLQVWDQFHFUHDWLRQFRGHWRWKHVKDUHGVHWXSDVLWLVUHSHDWHGO\
UHTXLUHGE\HYHU\WHVWRIWKHJURXS
..
setup: function() {
this.fixture = document.createElement("div");
document.getElementsByTagName('body')[ 0 ].appendChild(
this.fixture );
this.form = new SignInForm( this.fixture );
},
..
:ULWHWHVWVWRDVVHUWRQWKHSignInFormPHWKRGV
// Test hide method
QUnit.test( "hide method", function( assert ){
32
Instant Testing with QUnit
this.form.hide();
assert.strictEqual( this.fixture.style.display, "none" );
});
// Test show method
QUnit.test( "show method", function( assert ){
this.form.show();
assert.notStrictEqual( this.fixture.style.display, "none" );
});
/RDGWKHWHVWUXQQHULQDEURZVHUDQGH[DPLQHWKHUHVXOWVVKRZQLQWKH
IROORZLQJVFUHHQVKRW
7HVWLQJXVHUDFWLRQV0HGLXP
,QWKLVUHFLSHZHZLOOZULWHDVLPSOHFDOFXODWRUZLGJHW%\WHVWLQJZHZLOOVLPXODWHHQGXVHU
DFWLRQVDQGDVVHUWWKHLQWHQGHGDSSOLFDWLRQEHKDYLRU
Getting ready
%HIRUHMXPSLQJRQWKHWHVWZHGHÀQLWHO\QHHGDQDSSOLFDWLRQH[SHFWLQJXVHUDFWLRQV/HWLW
EHDVLPSOHFDOFXODWRUWKDWFDQQRWGRFRPSOH[RSHUDWLRQVEXWZLOOVXPXSWZRRIWKHVXSSOLHG
QXPEHUVDVVHHQLQWKHIROORZLQJVFUHHQVKRW
33
Instant Testing with QUnit
7RPDNHLWZRUNZHVXEVFULEHDKDQGOHUWRWKHclickHYHQWRQWKHCalculateEXWWRQ
7KHKDQGOHUWDNHVWKHYDOXHVRIWKHÀUVWDQGVHFRQGÀHOGVVXPVWKHPXSDQGSXWVWKH
UHVXOWLQWKHWKLUGÀHOG7KHLPSOHPHQWDWLRQRIWKHFRGHLVDVIROORZV
"strict mode";
this.utils = {
/**
* Fire a suplied event on a given element
* @param {object} el instance of HTMLElement
* @param {string} eventName
*/
trigger: function( el, eventName ) {
var e = document.createEvent("Event");
e.initEvent( eventName, true, true );
el.dispatchEvent( e );
},
/**
* Subscribe handler for event on a supplied element
* @param {object} el instance of HTMLElement
* @param {string} eventName
* @param {function} handlerCb
*/
subscribe: function( el, eventName, handlerCb ) {
el.addEventListener( eventName, function( e ) {
handlerCb( e );
}, false );
}
};
(function( global ){
var document = global.document,
utils = global.utils;
e.preventDefault();
sum.value = parseInt(num1.value, 10) +
parseInt(num2.value, 10);
});
});
}( this ));
34
Instant Testing with QUnit
/HW
VVDYHWKHVRXUFHFRGHLQWRWKHcalc.jsÀOHDQGORDGLWIURPWKH+70/OD\RXW
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Calc</title>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/
css/bootstrap-combined.min.css" rel="stylesheet">
<style>
.container {
margin: 32px;
}
</style>
</head>
<body>
<div class="container">
<form class="form-inline">
<input id="num1" type="text" class="input-small"
placeholder="Summand">
<span>+</span>
<input id="num2" type="text" class="input-small"
placeholder="Summand">
<span>=</span>
<input id="sum" type="text" class="input-small"
placeholder="Sum">
<button id="calc" type="button" class="btn btn-
primary">Calculate</button>
<button type="reset" class="btn btn-danger">Reset</button>
</form>
</div>
<script src="./js/calc.js"></script>
</body>
</html>
1RZZHUXQWKLV+70/LQDEURZVHUDQGWHVWLWPDQXDOO\:LWKQXPEHUVW\SHGLQWKHEXWWRQ
FOLFNHGDQGWKHUHVXOWUHFHLYHGZHDUHQRZUHDG\WRZULWHDQDXWRPDWHGWHVWIRUWKHDFWLRQV
ZHMXVWSHUIRUPHG
35
Instant Testing with QUnit
How to do it
6XEVFULEHDKDQGOHUWRWKH'20UHDG\HYHQWDQGJHWUHIHUHQFHVRQWKHLQYROYHG
HOHPHQWV7RPLPLFDXVHUW\SLQJQXPEHUVDQGFOLFNLQJWKHEXWWRQZHQHHGWRUHIHU
WRWKHLQSXWVDQGWKHEXWWRQ+RZHYHUHOHPHQWVZLOOEHDYDLODEOHRQO\ZKHQWKH
'20WUHHLVEXLOW6RZHREWDLQUHIHUHQFHVRQWKHHOHPHQWVLQWKHKDQGOHUZKLFKLV
FDOOHGRQWKH'20UHDG\HYHQW)RUWKDWZHXVHWKHKHOSHULQWURGXFHGLQWKHVDPSOH
DSSOLFDWLRQDVIROORZV
utils.subscribe( global, "load", function() {
var calc = document.getElementById("calc"),
num1 = document.getElementById("num1"),
num2 = document.getElementById("num2"),
sum = document.getElementById("sum");
});
'HÀQHDWHVWVFRSHDQGVLPXODWHWKHXVHUEHKDYLRU:LWKLQWKHVFRSHZHDVVLJQWHVW
YDOXHVWRLQSXWVDQGWULJJHUWKHFOLFNHYHQW6LQFHWKHDSSOLFDWLRQUHVSRQGVWRWKH
FOLFNHYHQWLPPHGLDWHO\ZHFDQV\QFKURQRXVO\DVVHUWWKDWWKHYDOXHLQWKHUHVXOWÀHOG
HTXDOVWKHVXPRIQXPEHUVZHVXSSOLHG
utils.subscribe( global, "load", function() {
var calc = document.getElementById("calc"),
num1 = document.getElementById("num1"),
num2 = document.getElementById("num2"),
sum = document.getElementById("sum");
36
Instant Testing with QUnit
/RDGWKHWHVWUXQQHULQDEURZVHUDQGH[DPLQHWKHUHVXOWVVKRZQLQWKH
IROORZLQJVFUHHQVKRW
5XQQLQJ48QLWLQWKHFRQVROH$GYDQFHG
,QWKLVUHFLSHZHZLOOUXQ48QLWWHVWVIURPWKHFRPPDQGOLQHE\XVLQJWKH3KDQWRP-6
KHDGOHVVEURZVHU
Getting ready
:H
YHOHDUQWKRZWRUXQ48QLWLQDEURZVHUDQGLW
VVXLWDEOHLQWKHEHJLQQLQJ+RZHYHULQDQ
DGYDQFHGGHYHORSPHQWF\FOHWKHWHVWVDUHEHLQJH[HFXWHGE\DWRROVXFKDVDSLHFHRIEXLOG
DXWRPDWLRQVRIWZDUHRUDVCSversion control system6RZHQHHGWRUXQRXUWHVWVLQWKH
FRQVROH7KDWFDQEHGRQHZLWK48QLWWHVWVXVLQJDKHDGOHVVEURZVHU
/HW
VWDNHPhantomJShttps://github.com/ariya/phantomjs)LUVWRIDOOZHKDYH
WRLQVWDOOWKHSDFNDJH7KDW
VTXLWHHDV\WRGRZLWKDSDFNDJHPDQDJHULQ0DF26RULQ8QL[
/LQX[)RUDQLQVWDQFHXVLQJ+RPHEUHZRQ0DF26\RXFDQGRLWOLNHWKLV
brew install phantomjs
37
Instant Testing with QUnit
2Q:LQGRZV\RXFDQVLPSO\GRZQORDGWKHH[HFXWDEOHÀOHIURPhttp://phantomjs.org/
download.htmlDQGDGGLWVORFDWLRQWRWKH:LQGRZVSDWKDVIROORZV
:HZLOODOVRQHHGWKH3KDQWRP-65XQQHUVFULSWhttps://github.com/ariya/
phantomjsZKLFKSOXJVLQWR48QLWWRWUDQVODWHWKHUHSRUWRXWSXWLQWR
FRPPDQGOLQHFRPSDWLEOHIRUPDW
How to do it
,QVWDOO3KDQWRP-6
'RZQORDG3KDQWRP-65XQQHUDQGVDYHLWDVrunner.js
5XQWKHWHVWVXLWHZLWKWKHIROORZLQJFRPPDQG
phantomjs runner.js test-suite.html
([DPLQHWKHUHVXOWVVKRZQLQWKHIROORZLQJVFUHHQVKRW
38
Instant Testing with QUnit
There's more
<RXPD\ÀQGSOHQW\RISURMHFWVRQ*LW+XEhttps://github.comXVLQJWKHTravis CI
https://travis-ci.orgFRQWLQXRXVLQWHJUDWLRQVHUYHU7UDYLV&,UHSRUWVWRWKHSURMHFW
PHPEHUVZKHQVRPHERG\EUHDNVWKHEXLOG,WPHDQVLI\RXPDNHVRPHFKDQJHVLQWKH
SURMHFWFRGHDQGXVHWKHpushFRPPDQGWRFRPPLWWR*LW+XE7UDYLV&,FDQDXWRPDWLFDOO\
FKHFNLIWKHFKDQJHGFRGHVWLOODGKHUHVWRWKHVW\OHJXLGHDQGOLQWHUUHTXLUHPHQWVLIWKHXQLW
WHVWVWLOOSDVVHVDQGVRRQ+RZGRHVLWZRUN":LWKHYHU\FRGHFRPPLW7UDYLV&,SXOOVGRZQ
WKHSURMHFW
VZRUNLQJGLUHFWRU\IURP*LW+XEDQGUXQVWKHGruntWDVNUXQQHUwww.gruntjs.
com*UXQWSHUIRUPVWKHWDVNVVXSSOLHGLQWKHFRQÀJXUDWLRQÀOHGruntfile.js$PRQJ
RWKHUWDVNV\RXFDQDVVLJQ48QLWWHVWV7UDYLV&,VWRUHVWKHWHVWUHVXOWVLQWKHEXLOGUHSRUWDQG
QRWLÀHVWKHFRPPLWLIWKHEXLOGIDLOV
/HW
VVHHKRZZHFDQWDNHDGYDQWDJHRIWKLVDSSURDFK
,QVWDOO1RGHMV)RU0DF26;ZHFDQDJDLQUHFRXUVHWR+RPHEUHZ
brew install node
,QVWDOODWLRQLQVWUXFWLRQVIRURWKHUSODWIRUPVFDQEHIRXQGRQWKLVSDJH
https://github.com/joyent/node/wiki/Installing-Node.js-via-
package-manager
,QVWDOO*UXQW,WFDQEHHDVLO\GRQHQRZWKDWZHKDYHWKHNode.jsSDFNDJHPDQDJHU
npm install -g grunt-cli
0RYHWRWKHSURMHFWZRUNLQJGLUHFWRU\
&RQÀJXUHWKHSURMHFWZLWKpackage.json
{
"name": "project-name",
"description": "Project description",
"version": "0.0.1",
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-qunit": "~0.2.1",
"qunitjs": "1.x"
}
}
39
Instant Testing with QUnit
,QVWDOOWKHUHTXLUHGGHSHQGHQFLHV
npm install
6HWXSWKHGruntWDVNWRÀUHXSWKH48QLWWHVWUXQQHULQtest-suite.html
module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-contrib-qunit");
grunt.initConfig({
qunit: {
all: ["tests/test-suite.html"]
}
});
grunt.registerTask("test", ["qunit"]);
grunt.registerTask("default", ["test"]);
};
([DPLQHWKHUHVXOWVVKRZQLQWKHIROORZLQJVFUHHQVKRW
Cross-browser-distributed
WHVWLQJ$GYDQFHG
7KLVUHFLSHZLOOJXLGH\RXRQDXWRPDWLQJFOLHQWVLGHFURVVSODWIRUPEURZVHUWHVWLQJXVLQJWKH
command-line tool Bunyip
40
Instant Testing with QUnit
Getting ready
,QFRQWUDVWWRWKHVHUYHUVLGHVRIWZDUH-DYD6FULSWDSSOLFDWLRQVDUHEHLQJH[HFXWHGRQWKHFOLHQW
VLGHDQGWKHUHIRUHGHSHQGRQWKHXVHUEURZVHU1RUPDOO\SURMHFWVSHFLÀFDWLRQLQFOXGHVWKHOLVW
RIWKHEURZVHUVDQGSODWIRUPVWKDWWKHDSSOLFDWLRQPXVWVXSSRUW7KHORQJHUWKHOLVWWKHKDUGHU
LVFURVVEURZVHUFRPSDWLELOLW\WHVWLQJ)RUH[DPSOHM4XHU\VXSSRUWVEURZVHUVRQGLIIHUHQW
SODWIRUPV7KHSURMHFWLVIXOO\WHVWHGLQHYHU\GHFODUHGHQYLURQPHQWZLWKHYHU\VLQJOHFRPPLW
7KDWLVSRVVLEOHWKDQNVWRWKHGLVWULEXWHGWHVWLQJWRRO7HVW6ZDUPswarm.jquery.org<RX
PD\DOVRKHDURIRWKHUWRROVVXFKDV-V7HVW'ULYHUcode.google.com/p/js-test-driver
RU.DUPDkarma-runner.github.io:HZLOOWDNH%XQ\LShttps://github.com/
ryanseddon/bunyipDVLWKDVVZLIWO\EHHQJDLQLQJSRSXODULW\UHFHQWO\
+RZGRHVLWZRUN"<RXODXQFKWKHWRROIRUDWHVWUXQQHU+70/DQGLWSURYLGHVWKHFRQQHFW
HQGSRLQWIP:portDQGODXQFKHVDORFDOO\LQVWDOOHGEURZVHULIFRQÀJXUHG$VVRRQDV
\RXÀUHXSWKHDGGUHVVLQDEURZVHUWKHFOLHQWLVFDSWXUHGE\%XQ\LSDQGWKHFRQQHFWLRQ
LVHVWDEOLVKHG:LWK\RXUFRQÀUPDWLRQ%XQ\LSUXQVWKHWHVWVLQHYHU\FRQQHFWHGEURZVHU
WRFROOHFWDQGUHSRUWUHVXOWV6HHWKHIROORZLQJÀJXUH
41
Instant Testing with QUnit
7RLQVWDOOWKHWRROW\SHLQWKHFRQVROHDVIROORZV
npm install -g bunyip
+HUHZHUHFRXUVHWRWKHNode.jsSDFNDJHPDQDJHUWKDWLVSDUWRINode.js6RLI\RXGRQ
W
KDYHNode.jsLQVWDOOHGÀQGWKHLQVWDOODWLRQLQVWUXFWLRQVRQWKHIROORZLQJSDJH
https://github.com/joyent/node/wiki/Installing-Node.js-via-package-
manager
1RZZHDUHUHDG\WRVWDUWXVLQJ%XQ\LS
How to do it
$GGWRWKH48QLWWHVWVXLWHtest-suite.htmlWKHIROORZLQJFRQÀJXUDWLRQRSWLRQ
WRSUHYHQWLWIURPDXWRVWDUWLQJEHIRUHWKHSOXJLQFDOOEDFNLVVHWXS
if (QUnit && QUnit.config) {
QUnit.config.autostart = false;
}
42
Instant Testing with QUnit
([DPLQHWKHUHVXOWVVKRZQLQWKHIROORZLQJVFUHHQVKRW
%XLOGLQJDZHESURMHFW$GYDQFHG
:KLOHZRUNLQJZLWKD-DYD6FULSWSURMHFWEXLOGPD\VRXQGRGGLW
VTXLWHUHOHYDQWKHUH8VLQJ
EXLOGDXWRPDWLRQVRIWZDUHZHFDQSHUIRUPDEXQFKRISURMHFWUHODWHGWDVNVZLWKDVLQJOH
FRPPDQG,W
VFRPPRQSUDFWLFHQRZDGD\VWRUXQWDVNVVXFKDV-DYD6FULSWOLQWLQJFRGH
VWDQGDUGYDOLGDWLRQ&66SUHSURFHVVLQJDQG$3,GRFXPHQWDWLRQXSGDWHZLWKDEXLOGVFULSW
PDQXDOO\RUXVLQJFRQWLQXRXVLQWHJUDWLRQWRROV7KLVUHFLSHVKRZVKRZWKH48QLWWHVWLQJWDVN
FDQEHDGGHGWRWKHSURMHFWEXLOGVFULSW
43
Instant Testing with QUnit
Getting ready
)RUWKLVWDVNZHZLOOXVHApache Ant (http://ant.apache.org/)IRUEXLOGDXWRPDWLQJ
2Q0DF26;$QWLVDYDLODEOHE\GHIDXOW2Q8QL[/LQX[LWLVDYDLODEOHLQSRUWVDQG\RXFDQXVH
DSDFNDJHPDQDJHUWRLQVWDOO$QW$VIRU:LQGRZV\RXFDQÀQGWKHLQVWDOODWLRQLQVWUXFWLRQVLQ
WKHRIÀFLDOPDQXDODWant.apache.org/manual/index.html
6LQFH$QWUXQV48QLWWHVWVRQWKHFRPPDQGOLQHZHZLOOXVH3KDQWRP-6DVLWLVGLVFXVVHG
LQWKHSUHFHGLQJVHFWLRQ+RZHYHULIZHDUHWRVDWLVI\WKLUGSDUW\VRIWZDUHH[HFXWLQJWKHEXLOG
VFULSWVXFKDVFRQWLQXRXVLQWHJUDWLRQWRROVZHQHHGWRPDNHWKH48QLWUHSRUWLQDVSHFLÀF
IRUPDW0RVWWRROVDFFHSWJUnit XML:HFDQWUDQVODWHWKH48QLWRXWSXWWR-8QLWIRUPDW
ZLWKDSOXJLQFDOOHGJUnit Loggerhttps://github.com/jquery/qunit-reporter-
junit6RZHDGGWRWKHWHVWUXQQHU+70/WKHSOXJLQ-DYD6FLSWPRGXOHDQGWKHIROORZLQJ
FRQÀJXUDWLRQRSWLRQ
QUnit.jUnitReport = function(report) {
console.log(report.xml);
};
%XWKHUHSRSVXSDQRWKHUSUREOHP7KH3KDQWRP-65XQQHUVFULSWQRZEUHDNV-8QLW;0/
ZLWKLWVRZQUHSRUWYHUVLRQ<RXFDQVLPSO\FRPPHQWRXWconsole.logRFFXUUHQFHVLQWKH
QUnit.done and QUnit.testDoneFDOOEDFNIXQFWLRQVDWWKHHQGRIrunner.js and save
LWVD\DVrunner-muted.js
How to do it
6HWXSWKHEXLOGVFULSWDVbuild.xmlLQWKHURRWRIWKHSURMHFWZRUNLQJGLUHFWRU\
<?xml version="1.0"?>
<!DOCTYPE project>
<project name="tree" basedir="." default="build">
<target name="build" description="runs QUnit tests using
PhantomJS">
<!-- Clean up output directory -->
<delete dir="./build/qunit/"/>
<mkdir dir="./build/qunit/"/>
<!-- QUnit Javascript Unit Tests -->
<echo message="Executing QUnit Javascript Unit Tests..."/>
</target>
44
Instant Testing with QUnit
</project>
([WHQGLWZLWKWKHEXLOGWDUJHWWRUXQ48QLWWHVWV
<?xml version="1.0"?>
<!DOCTYPE project>
<project name="tree" basedir="." default="build">
<target name="build" description="runs QUnit tests using
PhantomJS">
<!-- Clean up output directory -->
<delete dir="./build/qunit/"/>
<mkdir dir="./build/qunit/"/>
<!-- QUnit Javascript Unit Tests -->
<echo message="Executing QUnit Javascript Unit Tests..."/>
<exec executable="/usr/local/bin/phantomjs" output="./build/
qunit/qunit-results.xml">
<arg value="./vendors/Runner/runner-muted.js" />
<arg value="http://test.dev/qunit/tests/test11/index.html" />
</exec>
</target>
</project>
5XQ$QWLQWKHVDPHGLUHFWRU\ZKHUHbuild.xmlLVORFDWHG
ant
([DPLQHWKHUHVXOWVVKRZQLQWKHIROORZLQJVFUHHQVKRW
45
Instant Testing with QUnit
:HZLOOQRZLQVWDOOD-HQNLQV&,VHUYHUDQGVHWLWXSIRUD48QLWWHVWUXQQHUMRE
Getting ready
7KHEHVWZD\WRH[SODLQ&,LVWRVKRZLWLQDQH[DPSOH/HW
VDVVXPH\RXDUHRQHRIWKH
GHYHORSHUVZRUNLQJRQDSURMHFW<RX
UHXVLQJDYHUVLRQFRQWUROV\VWHPVR\RXUFROODERUDWLRQ
RQWKHFRGHJRHVÀQH<HWZKHQHYHUDVWDEOHUHOHDVHLVWDJJHG\RXLQHYLWDEO\UXQLQWR
SUREOHPVLQWHJUDWLRQKHOO <RXDWWHPSWWREXLOGWKHUHOHDVHDQGJHWVXUSULVHGZLWKWKH
UHVXOWV:KLOHZRUNLQJZLWKWKHIURQWHQGEXLOGDVDWHUPPD\VRXQGRGGEXWLWLV
QRQHWKHOHVVTXLWHUHOHYDQWKHUH'XULQJUHOHDVHDQXPEHURIWDVNVPXVWEHSHUIRUPHG
FRGHTXDOLW\DQDO\VLVFRGHVWDQGDUGYLRODWLRQFKHFNVWHVWV&66SUHSURFHVVLQJ&RIIH6FULSW
RU7\SH6FULSWFRPSLODWLRQ$3,GRFXPHQWDWLRQJHQHUDWLRQLPDJHRSWLPL]DWLRQ-6&66
FRQFDWHQDWLRQDQGFRPSUHVVLRQVHHWKHDUWLFOHH5bp Ant Build Script at https://github.
com/h5bp/ant-build-script,WJHWVHYHQEHWWHURQWKHVHUYHUVLGHVRXUFHV
SURMHFWVL]HLVPHDVXUHGVRIWZDUHPHWULFVDUHFDOFXODWHGPHVVGHWHFWLRQLVSHUIRUPHG
DQGFRGHGXSOLFDWHVDUHLGHQWLÀHGVHHTemplate for Jenkins Jobs for PHP Projects at
http://jenkins-php.org/index.html7KDWLVZKDWZHFDOODEXLOG:HDXWRPDWHWKH
EXLOGSURFHVVLQJXVLQJVSHFLDOVRIWZDUHVXFKDV$SDFKH$QW0DYHQ*UXQW3KLQJDQG%DVK
&,VHUYHUOLVWHQVWRWKHXSGDWHVRQWKHYHUVLRQFRQWUROUHSRVLWRU\:KHQHYHU\RXFRPPLW
WKHVHUYHUSXOOVWKHZRUNLQJYHUVLRQRIWKHSURMHFWDQGUXQVWKHbuild script,WFROOHFWVWKH
UHSRUWVRIWKHWRROVSHUIRUPLQJWKHEXLOGWDVNVDQGOHWV\RXNQRZE\HPDLOZKHWKHUWKHEXLOG
ZDVVXFFHVVIXORUQRW7KXV\RXNQRZLPPHGLDWHO\LIWKHFKDQJHV\RXPDGHFDXVHDQ\
WURXEOHGXULQJLQWHJUDWLRQ
7RJHWDEHWWHUJULSRQ&,ZHLQVWDOO-HQNLQVVHUYHUDQGGHÀQHDMREUXQQLQJ48QLWWHVWV
46
Instant Testing with QUnit
How to do it
,QVWDOO-HQNLQV,I\RXZRUNXQGHU0DF26;MXVWUXQ
sudo brew install jenkins
7RLQVWDOO-HQNLQVRQ8QL[/LQX[SOHDVHÀQGLQVWUXFWLRQVDWhttps://wiki.
jenkins-ci.org/display/JENKINS/Use+JenkinsDQGIRU:LQGRZVDW
https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+
as+a+Windows+service
(QDEOHWKH*LWSOXJLQ
1DYLJDWHWRJenkins | Manage Jenkins | Manage Plugins | Available and
select Git plugin
&RQILUP\RXUFKRLFHE\FOLFNLQJRQWKHOKEXWWRQDWWKHERWWRPRIWKHSDJH
47
Instant Testing with QUnit
$GGDQHZMREWR-HQNLQV
(QWHUWKHJenkins DashboardSDJHDQGFOLFNRQWKHAdd new jobOLQN
6SHFLI\WKHSURMHFWjobQDPHLQWKHGLVSOD\HGIRUP
&KRRVHBuild a free-style software projectDVVKRZQLQWKH
IROORZLQJVFUHHQVKRW
48
Instant Testing with QUnit
([DPLQHWKHQH[WVFUHHQDVVKRZQLQWKHIROORZLQJVFUHHQVKRW
49
Instant Testing with QUnit
&RQÀJXUHWKHMREIRUFRQWLQXRXVLQWHJUDWLRQ
2SHQWKHSURMHFWSDJHDQGFOLFNRQConfigure
50
Instant Testing with QUnit
&RPPLWDFKDQJHLQ\RXUSURMHFWDQGSXVKLWWRWKH*LWVHUYHUDQGJHWDXWRPDWLFDOO\
QRWLÀHGLIWKH48QLWWHVWVIDLO
51
Instant Testing with QUnit
There's more
,I\RXDUHDQDXWKRUL]HGjenkinsXVHUDWWKH*LWVHUYHU\RXZRQ
WEHDEOHWRPDNHDEXLOG
XVLQJWKH*LWSOXJLQ<RXQHHGDSDLURI66+NH\VWRH[FKDQJHEHWZHHQWKHgit and jenkins
XVHUVDVGHVFULEHGLQWKHGit DocumentationVHFWLRQhttp://git-scm.com/book/ch4-
4.html,I\RXDUHRQD0DFWKHDUWLFOHSetting up a Git Server on Mac OSXhttp://blog.
smitec.net/posts/setting-up-a-git-server-on-osx/ZLOOEHRIJUHDWKHOS
7KHQZHFRS\GHULYHGWKHjenkins_rsa.pubÀOHWRWKH*LWVHUYHU
scp jenkins_rsa.pub git@our-git-server.com:/home/git/.ssh
:HDOVRKDYHWRHGLW~/.ssh/configWRFRQÀJXUHWKH66+FOLHQW
Host git@our-git-server.com
IdentityFile ~/.ssh/jenkins_rsa
1RZ-HQNLQVLVVXSSRVHGWREHDXWRPDWLFDOO\DXWKRUL]HGRQWKH*LWVHUYHU
52
Thank you for buying
Instant Testing with QUnit
$ERXW3DFNW3XEOLVKLQJ
3DFNWSURQRXQFHG
SDFNHG
SXEOLVKHGLWVÀUVWERRNMastering phpMyAdmin for Effective MySQL
ManagementLQ$SULODQGVXEVHTXHQWO\FRQWLQXHGWRVSHFLDOL]HLQSXEOLVKLQJKLJKO\IRFXVHG
ERRNVRQVSHFLÀFWHFKQRORJLHVDQGVROXWLRQV
2XUERRNVDQGSXEOLFDWLRQVVKDUHWKHH[SHULHQFHVRI\RXUIHOORZ,7SURIHVVLRQDOVLQDGDSWLQJDQG
FXVWRPL]LQJWRGD\
VV\VWHPVDSSOLFDWLRQVDQGIUDPHZRUNV2XUVROXWLRQEDVHGERRNVJLYH\RXWKH
NQRZOHGJHDQGSRZHUWRFXVWRPL]HWKHVRIWZDUHDQGWHFKQRORJLHV\RX
UHXVLQJWRJHWWKHMREGRQH
3DFNWERRNVDUHPRUHVSHFLÀFDQGOHVVJHQHUDOWKDQWKH,7ERRNV\RXKDYHVHHQLQWKHSDVW2XU
XQLTXHEXVLQHVVPRGHODOORZVXVWREULQJ\RXPRUHIRFXVHGLQIRUPDWLRQJLYLQJ\RXPRUHRIZKDW
\RXQHHGWRNQRZDQGOHVVRIZKDW\RXGRQ
W
3DFNWLVDPRGHUQ\HWXQLTXHSXEOLVKLQJFRPSDQ\ZKLFKIRFXVHVRQSURGXFLQJTXDOLW\
FXWWLQJHGJHERRNVIRUFRPPXQLWLHVRIGHYHORSHUVDGPLQLVWUDWRUVDQGQHZELHVDOLNH
)RUPRUHLQIRUPDWLRQSOHDVHYLVLWRXUZHEVLWHwww.packtpub.com
:ULWLQJIRU3DFNW
:HZHOFRPHDOOLQTXLULHVIURPSHRSOHZKRDUHLQWHUHVWHGLQDXWKRULQJ%RRNSURSRVDOVVKRXOGEH
sent to author@packtpub.com,I\RXUERRNLGHDLVVWLOODWDQHDUO\VWDJHDQG\RXZRXOGOLNHWR
GLVFXVVLWÀUVWEHIRUHZULWLQJDIRUPDOERRNSURSRVDOFRQWDFWXVRQHRIRXUFRPPLVVLRQLQJHGLWRUV
ZLOOJHWLQWRXFKZLWK\RX
:H
UHQRWMXVWORRNLQJIRUSXEOLVKHGDXWKRUVLI\RXKDYHVWURQJWHFKQLFDOVNLOOVEXWQRZULWLQJ
H[SHULHQFHRXUH[SHULHQFHGHGLWRUVFDQKHOS\RXGHYHORSDZULWLQJFDUHHURUVLPSO\JHWVRPH
DGGLWLRQDOUHZDUGIRU\RXUH[SHUWLVH
JavaScript Unit Testing
,6%13DSHUEDFNSDJHV
<RXUFRPSUHKHQVLYHDQGSUDFWLFDOJXLGHWRHIIHFLHQWO\
SHUIRUPLQJDQGDXWRPDWLQJ-DYD6FULSWXQLWWHVWLQJ
/HDUQDQGXQGHUVWDQGXVLQJSUDFWLFDO
H[DPSOHVV\QFKURQRXVDQGDV\QFKURQRXV
-DYD6FULSWXQLWWHVWLQJ
&RYHUWKHPRVWSRSXODU-DYD6FULSW8QLW7HVWLQJ
)UDPHZRUNVLQFOXGLQJ-DVPLQH<8,7HVW48QLW
DQG-V7HVW'ULYHU
$XWRPDWHDQGLQWHJUDWH\RXU-DYD6FULSW8QLW
7HVWLQJIRUHDVHDQGHIÀFLHQF\
M4XHU\8,7KH8VHU
,QWHUIDFH/LEUDU\IRUM4XHU\
,6%13DSHUEDFNSDJHV
%XLOGKLJKO\LQWHUDFWLYHZHEDSSOLFDWLRQV
ZLWKUHDG\WRXVHZLGJHWVIURPWKHM4XHU\8VHU
,QWHUIDFH/LEUDU\
3DFNHGZLWKH[DPSOHVDQGFOHDUH[SODQDWLRQV
RIKRZWRHDVLO\GHVLJQHOHJDQWDQGSRZHUIXO
IURQWHQGLQWHUIDFHVIRU\RXUZHEDSSOLFDWLRQV
$VHFWLRQFRYHULQJWKHZLGJHWIDFWRU\LQFOXGLQJDQ
LQGHSWKH[DPSOHRQKRZWREXLOGDFXVWRPM4XHU\
8,ZLGJHW
8SGDWHGFRGHZLWKVLJQLÀFDQWFKDQJHVDQGÀ[HV
WRWKHSUHYLRXVHGLWLRQ