WEB开发网      濠电姷鏁告慨鐑藉极閸涘﹥鍙忛柣鎴f閺嬩線鏌涘☉姗堟敾闁告瑥绻橀弻锝夊箣閿濆棭妫勯梺鍝勵儎缁舵岸寮诲☉妯锋婵鐗嗘慨娑氱磽娴e搫鈻堢紒鐘崇墵瀵顓奸崼顐n€囬梻浣告啞閹歌顫濋妸鈺佺闁靛繒濮Σ鍫熺箾閸℃ê濮囨い搴㈡崌濮婃椽宕ㄦ繝鍌氼潓閻庢鍠栭悥濂哥嵁閺嶎厼绠涙い鏃傚亾閿涘繘姊洪崨濠冨瘷闁告洦鍋呴悾顒勬⒒娴e摜鏋冩い顐㈩樀瀹曞綊宕稿Δ鈧粻鏍煃閸濆嫬鏆婇柛瀣崌瀹曠兘顢橀悙鎰╁灪閵囧嫰濡烽敂鍓х杽濠殿喖锕ら幖顐f櫏闂佹悶鍎滈埀顒勫磻閹炬緞鏃堝川椤撶媴绱遍梻浣筋潐瀹曟﹢顢氳椤㈠﹪姊绘担鍛婂暈婵炶绠撳畷褰掑箥椤斿彞绗夊┑鐐村灟閸ㄦ椽鎮¢弴鐔翠簻闁规澘澧庣粙鑽ょ磼閳ь剟宕橀埞澶哥盎闁硅壈鎻槐鏇熸櫏婵犳鍠栭敃銊モ枍閿濆洤鍨濇繛鍡楃箚閺嬪酣鏌熼鍡楀暙椤ユ劙姊婚崒娆戭槮闁硅姤绮嶉幈銊╂偨缁嬭法顦┑鐐叉閸旀帞鎹㈤崱娑欑厽闁靛繆鎳氶崷顓犵焼閻庯綆鍋佹禍婊堟煛瀹ュ啫濮€濠㈣锕㈤弻娑㈡倷椤忓嫬顫囧┑顔硷攻濡炶棄螞閸愵煁褰掑Χ閸℃瑢濮囬梺鐟板槻閹虫﹢鐛幘璇茬鐎广儱鎷嬪Λ婊堟⒒閸屾瑧顦︽繝鈧柆宥呯?闁靛牆顦埀顒€鍟村畷鍗炩槈濡厧骞堥梻浣告贡閸庛倝銆冮崱娑欏亗闁哄洢鍨洪悡娑㈡煕閵夛絽鍔氬┑锛勫帶闇夋繝濠傚閻鏌曢崶褍顏紒鐘崇洴閺佹劙宕ㄩ鐘垫綁闂傚倷绀侀幖顐e緞閸ヮ剙鐒垫い鎺嗗亾缁剧虎鍙冨鎶藉幢濞戞瑥鈧敻鏌ㄥ┑鍡涱€楀褌鍗抽弻锟犲幢濞嗗繆鏋呴梺鍝勭潤閸曨偒鍤ゅ┑鐐叉閸ㄧ敻宕哄畝鍕拺闂傚牊绋掗ˉ鐐烘偨椤栨稑娴柨婵堝仜閳规垹鈧綆鍋勬禍妤呮煙閼圭増褰х紒鎻掋偢閹粙鎳¢妶鍥╋紳婵炶揪缍€椤曟牕鈻撻弴銏$厱闁靛ǹ鍎虫禒銏°亜椤愩垻绠崇紒杈ㄥ笒铻i悹鍥ф▕閳ь剚鎸剧槐鎾存媴閸︻厸妲堝銈嗗灥閹冲酣鍩㈤幘娲绘晣闁绘劏鏅滈弬鈧俊鐐€栧褰掑几婵犳艾绀傛い鎺戝€荤壕濂告煟濡寧鐝€规洖鐭傞弻鏇㈠幢閺囩媭妲銈庡亝缁诲牓鐛崶銊﹀闁稿繐顦伴悗鍛婄節閻㈤潧啸闁轰礁鎲¢幈銊╊敇閵忕姷锛涢梺瑙勫礃缁夘喛銇愰幒鎾存珳闂佹悶鍎弬鍌炲焵椤掆偓閿曨亪寮婚敓鐘茬劦妞ゆ帊鑳堕々鐑芥倵閿濆骸浜為柛妯挎閳规垿鍩ラ崱妤冧画濡炪倖鍨堕悷鈺佺暦閻㈢鍋撻敐搴″幋闁稿鎸鹃幉鎾礋椤掆偓娴犫晠姊虹粙鎸庡攭缂侇噣绠栭幃姗€宕橀瑙f嫼缂傚倷鐒﹂埣銈夘敂閸曢潧娈ㄩ梺鍓插亝濞叉牠鎮块鈧弻锝夊箛椤旇姤姣勭紒鐐劤閵堟悂寮诲☉姘勃闁绘劦鍓涘▓銈夋煛娴e摜澧﹂柟顔筋殘閹叉挳宕熼鍌ゆО缂傚倷绶¢崰鏍崲濡寧顥ら梺璇查叄濞佳囧箺濠婂吘娑㈩敍閻愬鍘靛銈嗙墪濡梻绮堟担鍦浄妞ゆ洍鍋撻柟顔筋殔閳绘捇宕归鐣屼邯闂備胶绮悧婊堝储瑜旈幃楣冩倻閼恒儱浜楅柟鐓庣摠钃辨い顐㈢Т閳规垿鍩ラ崱妤冧户闁荤姭鍋撻柨鏇炲€归崐鐢碘偓瑙勬礀濞层劎澹曟禒瀣厱閻忕偛澧介幊鍛存煕閺傝法校闁靛洤瀚版俊鎼佸Ψ閿旂粯顥i梻浣风串缁插墽鎹㈤崼銉у祦闁哄秲鍔嶆刊瀛樻叏濠靛棙婀伴柟韫嵆濮婄粯鎷呴搹鐟扮濠碘槅鍋勯崯纾嬫"闂佽宕橀褍效閺屻儲鍊甸柨婵嗛閺嬬喖鏌i幘璺烘瀾濞e洤锕俊鍫曞磼濮橆偄顥氶梻鍌欒兌缁垶銆冮崨顓囨稑螖閸涱厾鍘洪梺鍦亾缁剁偤寮崼婵嗙獩濡炪倖妫侀~澶屸偓姘偢濮婃椽鎳¢妶鍛呫垺绻涢懠顒€鈻堥柛鈹惧亾濡炪倖甯掗崯顖炴偟椤忓牊鐓熼煫鍥э工娴滈箖姊婚崒姘偓椋庣矆娓氣偓楠炴牠顢曢敃鈧粻鐘绘煙闁箑骞楅柛娆忕箻閺岀喓绱掗姀鐘崇亶闂佺ǹ顑傞弲鐘诲蓟閿濆围閹艰揪绱曟禒婊勭箾鐎涙ḿ鐭婄紓宥咃躬瀵鎮㈤崗鐓庘偓缁樹繆椤栨繂浜归柣锝嗘そ濮婃椽宕崟顒€娅ょ紓浣筋嚙閻楀棝锝炶箛鎾佹椽顢旈崪浣诡棃婵犵數鍋為崹鍫曟嚌妤e啨鈧倿宕崟銊︽杸闂佸疇妫勫Λ妤佺濠靛鐓熼柣鏂垮级濞呭﹪鏌曢崱鏇狀槮闁宠閰i獮鎺楀籍閸屾稒绶梻鍌欑閹碱偊宕锕€纾瑰┑鐘崇閸庡﹪鏌涢鐘插姕闁抽攱鍨堕幈銊╂偡閻楀牊鎮欓梺璇茬箰瀵墎鎹㈠☉娆愬闁告劖褰冮顐c亜閳哄啫鍘撮柡灞诲姂瀵挳鎮欏ù瀣壕鐟滅増甯楅崑鍌炴煛閸ャ儱鐏柣鎾崇箰閳规垿鎮欓懠顑胯檸闂佸憡鏌i崐婵嬪蓟濞戙垹鐓涢悗锝庡墰钃辨俊鐐€戦崝濠囧磿閻㈢ǹ绠栨繛鍡樻尭缁狙囨煙鐎涙ḿ绠ユ繛鍏肩娣囧﹪濡堕崶顬儵鏌涚€n剙浠遍柡浣稿暣婵偓闁靛牆鍟犻崑鎾存媴缁洘鐎婚梺鍦亾濞兼瑥鈻撻幇鐗堚拺闁告劕寮堕幆鍫熴亜閹存繃鍠橀柣娑卞櫍婵偓闁靛牆妫岄幏濠氭⒑缁嬫寧婀伴柣鐕傚缁﹪鎮ч崼娑楃盎濡炪倖鍔戦崺鍕i幖浣圭厽闁挎繂鎳庡Σ濠氭懚閿濆鐓犳繛鏉戭儐濞呭洭鏌i幘鎰佸剰妞ゎ亜鍟存俊鍫曞幢濮楀棙鈷栭梻浣芥硶閸犲棝宕曢懠顒傜焿鐎广儱鐗勬禍褰掓煙閻戞ɑ灏甸柛妯兼暬濮婅櫣绱掑Ο铏逛桓闁藉啴浜堕幃妯跨疀閿濆懎绠归梻鍥ь槹缁绘繃绻濋崒姘缂備礁顦遍崕銈夊箞閵婏妇绡€闁告侗鍣禒鈺冪磽娴d粙鍝洪悽顖涘笩閻忔帡姊洪幆褏绠婚柍褜鍓氱粙鎺椼€佸鈧濠氬磼濞嗘垵濡介柣搴g懗閸忕姴鎼鍏煎緞婵犲嫭鐓f繝鐢靛仦閸ㄥ墎鍒掓惔銏㈩洸闂侇剙绉甸埛鎺懨归敐鍛暈闁哥喓鍋炵换娑氭嫚瑜忛悾鐢碘偓瑙勬礀缂嶅﹪寮婚崱妤婂悑闁告侗鍨伴獮鍫ユ⒒娴d警鏀伴柟娲讳邯濮婁粙宕熼娑樹簵濠电偛妫欓幐濠氭偂閻樺磭绠鹃柡澶嬪焾閸庢劖绻涢崨顓熷櫣闂囧鏌eΟ铏癸紞闁活厼锕弻宥囨喆閸曨偆浼岄梺鎼炲姂缁犳牠骞冨▎鎾村癄濠㈣泛顦崹婵嬫⒒閸屾瑦绁版い鏇熺墵瀹曟澘螖閸涱偀鍋撻崘顔煎窛闁哄鍨归崣鈧┑鐘灱閸╂牠宕濋弴鐘典笉闁规儼濮ら悡娆撴煙椤栧棗鑻▓鍫曟偡濠婂嫭绶叉繛灞傚妿濡叉劙骞樼拠鑼紲濠电偛妫欓崹鑲╃玻濡ゅ懏鈷戦柛婵勫劚鏍¢梺缁橆殘婵炩偓闁靛棔绶氬浠嬵敇閻愯尙鐛╅梻浣告贡閳峰牓宕㈡禒瀣柧闁挎繂顦伴埛鎴犵磼鐎n厽纭剁紒鐘冲▕閺屾稑螣閻樺弶鍣烘い鎰矙閺岋綁骞囬鍓х槇缂備浇顕уΛ娆撳Φ閸曨垰鍐€闁靛ě鍛帓闂佹眹鍩勯崹杈╃矙閹烘梹宕叉繛鎴欏灩瀹告繃銇勯幘璺烘瀻闁哄濮撮埞鎴︻敊绾嘲濮涚紓渚囧櫘閸ㄥ爼鐛箛娑樺窛閻庢稒锚娴狀參姊绘笟鍥у伎缂佺姵鍨甸埢鎾斥攽閸垻锛濋梺绋挎湰閻燂妇绮婇悧鍫涗簻闁哄洤妫楀ú銈囧瑜版帗鐓曟い顓熷灥濞呮﹢鏌涢妶鍡樼缂佽鲸鎸婚幏鍛嫻椤栨粎绐楃紓鍌欒濡狙囧磻閹剧粯鈷掑ù锝堫潐閸嬬娀鏌涙惔顔肩仸鐎规洘绻傞濂稿川椤忓懐鈧椽姊洪幖鐐插姶闁告挻宀搁崺娑㈠箣閻樼數锛滈柣搴秵閸嬪嫰顢氬⿰鍕瘈闁逞屽墴楠炲秹顢欓崜褝绱查梺璇插嚱缂嶅棝宕戦崨顓犳殾鐎光偓閳ь剟鍩€椤掑喚娼愭繛鎻掔箻瀹曡绂掔€n亞鐣烘繛瀵稿Т椤戝懎顔忓┑鍡忔斀闁绘ɑ褰冮鈺傤殽閻愭惌娈滄慨濠冩そ閹兘寮堕幐搴♀偓顖炴⒑娴兼瑧绉靛ù婊庝簻閻i鎲撮崟顓犵槇濠殿喗锕╅崜娑㈠储閹扮増鈷戦柛婵嗗閸屻劑鏌涢妸锔姐仢闁诡噯绻濇俊鐑芥晜閽樺浼庢繝纰樻閸ㄤ即鎮樺┑瀣亗闁规壆澧楅悡鐔兼煙閹规劖纭鹃柡瀣洴閺岋綁骞欓崘銊ゅ枈閻庤娲栭悥鍏间繆閻戣棄唯闁靛鍎涢幋鐘电=闁稿本鐟чˇ锔姐亜閹存繄澧曢柣锝囧厴閹粙宕归顐g稐闂備礁婀遍崕銈咁潖閼姐倕顥氶柛蹇涙?缁诲棙銇勯弽銊х畵濞存粌缍婇弻锝夋晲閸噥浠╃紓浣介哺閹稿骞忛崨瀛樻優闁荤喐澹嗛鑲╃磽閸屾瑦绁版い鏇嗗洦鍋嬮柛鈩冪⊕閸嬧晝鈧懓瀚伴崑濠傖缚閵娾晜鐓冪憸婊堝礈濮橆厾鈹嶅┑鐘插亞濞兼壆鈧厜鍋撳┑鐘插敪閵娧呯=闁稿本鐟︾粊鏉款渻閺夋垶鎲搁柟骞垮灲瀹曠厧鈹戦幇顓犵▉缂傚倸鍊烽悞锕佹懌婵犳鍨伴顓犳閹烘垟妲堟慨妤€妫楅崜鏉库攽閻愯尙澧涢柛鏃€鐟ラ~蹇撁洪鍕啇闂佺粯鍔栬ぐ鍐€栭崱娑欌拺闁告稑饪村▓鏃堟煕閻旈攱鍋ラ柟顕€绠栭幃婊呯驳鐎n偅娅栭梻浣虹帛閸旀ḿ浜稿▎鎰垫闁搞儺鍓氶埛鎴︽煟閻旂厧浜伴柛銈囧枎閳规垿顢氶埀顒€岣胯閸┿垽骞樺ǎ顒€浜濋梺鍛婂姀閺備線骞忛搹鍦=闁稿本鐟ч崝宥夋嫅闁秵鐓冮梺鍨儏濞搭噣鏌$仦鐣屝㈤柣锝忕節楠炲秹顢欑亸鏍у緧闂佽瀛╅鏍闯椤曗偓瀹曟垶绻濋崒婊勬闂佸湱鍎ら〃鍡涘磹閻戣姤鍊甸柣銏㈡瑜版帞宓侀柛顐犲劜閳锋帒霉閿濆洦鍤€闁崇粯娲熼弻鈩冪瑹閸パ勭彎閻庤娲橀崹鍧楃嵁濡偐纾兼俊顖滃帶楠炴劙姊绘担鍛婂暈濞撴碍顨婂畷鏉款潩鐠鸿櫣鐤囬梺鍛婁緱閸犳洜鎹㈤崱娑欑厱婵炲棗娴氬Σ绋库攽椤斿吋鍠橀柟钘夌埣閺佹劖寰勭€n亙鍝楁繝鐢靛仦閸ㄥ墎鏁幒鎾存珷闁哄被鍎查悡娑㈡煕鐏炵虎娈斿ù婊堢畺濮婂宕掑顑藉亾閻戣姤鍤勯柛鎾茬閸ㄦ繃銇勯弽顐粶缂佲偓婢舵劖鐓涢柛銉㈡櫅閳ь剨缍侀幃銏ゅ传閵壯呮闂備焦鎮堕崕婊堝礃閳轰礁濮冮梻鍌氬€烽懗鍓佸垝椤栫偛钃熼柕濞炬櫆閸庡秵绻濋棃娑卞剰缂備讲鏅犻弻銈夊箒閹烘垵濮屾繛瀛樼矋缁捇寮婚敓鐘茬闁靛⿵绠戦ˇ鈺侇渻閵堝啫鍔氭い锔炬暬瀵鈽夐姀鐘愁棟闁荤姴娲︾粊鎾磻閹炬枼鏀介悗锝庝簽椤旀垿姊洪崜鎻掍簼婵炲弶锕㈠畷鎰版倻閼恒儳鍘介梺鐟邦嚟閸嬪秶绱撳鑸电厱婵せ鍋撳ù婊嗘硾椤繐煤椤忓嫪绱堕梺鍛婃处閸撴瑩宕戝澶嬧拺闁告稑锕ラ悡銉╂煛閸偄澧寸€殿喗鐓″畷濂稿即閵婏附娅栭梻浣虹帛閸旀洟顢氶銏犲偍闁告鍋愰弨浠嬫煟閹邦剙绾фい銉︾矌缁辨帞绱掑Ο铏诡儌缂備緡鍠氱划顖滄崲濠靛棭娼╂い鎾跺Т楠炴劙姊虹拠鑼闁稿鍠栧鏌ヮ敃閿濆棙鐝¢梻浣筋嚙濮橈箓锝炴径濞掓椽鏁冮崒姘憋紱婵犵數濮撮崐濠氬汲閿曞倹鐓熼柡鍐ㄥ€甸幏锟犳煛娴e憡顥㈤柡灞界Х椤т線鏌涢幘瀵告噰闁糕斂鍨归鍏煎緞鐎n偅鐝抽梻浣规偠閸庮噣寮插┑瀣櫖婵犻潧娲ㄧ粻楣冨级閸繂鈷旂紒瀣吹閹叉悂寮堕崹顔芥缂備礁鍊哥粔褰掑箖濞嗘搩鏁嗛柛灞剧矌濡插洭姊绘笟鈧ḿ褎顨ヨ箛鏇炵筏闁告挆鍕幑闂佺粯鍔﹂崗娆愮濠婂牊鐓欓悗娑欋缚缁犳牜鈧懓鎲$换鍕閹烘鏁婇柛鎾楀啰顐奸梻渚€娼ч悧鐐电礊娴e摜鏆︽慨妞诲亾闁糕晪绻濆畷姗€濡搁妷褜鍚嬮梻鍌氬€峰ù鍥敋瑜忛埀顒佺▓閺呮繄鍒掑▎鎾崇闁瑰濮寸粻鐢告煟閻樺厖鑸柛鏂垮缁嬪顓奸崱妯哄伎濠碉紕鍋犻褎绂嶆ィ鍐┾拺闁告繂瀚~锕傛煕閺傝法鐒搁柛鈹垮劜瀵板嫭绻涢姀銏犳瀾鐎垫澘瀚伴幆鍌炲传閵夘灖鎴︽⒑闂堟稒鎼愰悗姘卞娣囧﹪骞栨担瑙勬珳闂佸憡渚楅崢鑹邦杺闂傚倸鍊峰ù鍥敋閺嶎厼绐楁俊銈呮噺閸嬶繝鏌嶉崫鍕櫡闁逞屽厸缁舵艾顕i鈧畷鐓庘攽閸偅效濠碉紕鍋戦崐鏍箰閼姐倖宕查柛鏇ㄥ幘閻棝鏌涢弴銊ョ仭闁抽攱甯¢弻娑氫沪閸撗勫櫗缂備椒鑳舵晶妤呭Φ閸曨垰鍗抽柣鏂挎惈閳峰矂鎮楃憴鍕;闁告鍟块锝嗙鐎e灚鏅濋梺闈涚墕濞村倸危缁嬪簱鏀介柣妯虹仛閺嗏晛鈹戦鑺ュ唉鐎规洘鍔栫换婵嗩潩椤掍浇绶㈤梻浣瑰濞叉牠宕愯ぐ鎺撳亗婵炲棙鍔戞禍婊堟煛瀹ュ骸浜滃ù鐘崇矊闇夋繝濠傛噹椤g厧菐閸パ嶈含闁瑰磭濮甸敍鎰攽閸℃﹩鍞查梻鍌欑閻ゅ洭锝炴径鎰瀭闁秆勵殔閺勩儵鏌涢弴銊ョ仩缂佲偓閸愵喗鐓忓┑鐐戝啯鍣烽柛瀣р偓鏂ユ斀闁挎稑瀚禍濂告煕婵炲灝鈧繂鐣烽姀掳鍋呴柛鎰╁妿椤ρ冣攽閳藉棗鐏熼悹鈧敃鍌氬惞闁哄洢鍨洪崑锝夋煕閵夛絽濡块柕鍫濈摠娣囧﹪骞撻幒鏂库叺闂佸搫鏈ú婵堢不濞戙垹鍗抽柣鎴濇缂嶅矂姊绘担绋挎毐闁圭⒈鍋婇獮濠呯疀濞戞瑥浜楅棅顐㈡处閹尖晠鎮㈤崱娑欏仯濡わ附瀵ч鐘差熆瑜庡ú鐔煎蓟濞戙垹绫嶉柍褜鍓熼獮鎰板箮閽樺鎽曞┑鐐村灟閸ㄧ懓螞濡崵绠鹃柛鈩冪懃娴滄儳螖閺冨倻纾介柛灞剧懄缁佹澘顪冪€涙ɑ鍊愭鐐村姈缁绘繂顫濋鍌ゅ數闂備礁鎲℃笟妤呭垂閹惰姤鍎楁繛鍡樻尭缁犲綊鎮楀☉娅虫垹浜搁鐏荤懓饪伴崼銏㈡毇闂佸搫鏈粙鎴﹀煘閹达箑绀嬫い鎰╁灩琚橀梻鍌欑劍濡炲潡宕㈡禒瀣濡わ絽鍟粻鐔兼煙闂傚鍔嶉柛瀣儔閺屾盯顢曢敐鍥╃暭闂佽崵鍠嗛崝鎴濐潖濞差亜浼犻柛鏇㈡涧閸擃喚绱撴担钘夌厫鐎光偓缁嬫鍤曞┑鐘崇閸嬪嫰鏌i幘铏崳妞わ富鍙冮幃宄扳堪閸愵亞顔婇梺杞扮贰閸犳牠鍩ユ径鎰潊闁挎稑瀚獮鎰版⒒娴e憡鍟炲〒姘殜瀹曟澘螖閸涱厾锛欓梺瑙勫婢ф鎮″☉銏″€堕柣鎰邦杺閸ゆ瑥鈹戦鐓庘偓鍧楀蓟閻旂⒈鏁婇柛婵嗗閸嬫挸鈹戦崱娆愭闂佸湱鍎ら崹鐔肺i崼鐔稿弿婵°倐鍋撴俊顐f⒒濡叉劙鏁撻敓锟� ---闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚敐澶婄闁挎繂鎲涢幘缁樼厱濠电姴鍊归崑銉╂煛鐏炶濮傜€殿噮鍣e畷濂告偄閸涘⿴鍞堕梻鍌欒兌鏋い鎴濇楠炴劙宕滆閸ㄦ繃銇勯幘璺轰汗婵℃彃鐗婃穱濠囶敍濮橆厽鍎撶紓浣哄Ь椤曆囧煘閹达附鍊烽柛娆忣槴閺嬫瑦绻涚€涙ḿ鐭嬬紒顔芥崌楠炲啴鍨鹃弬銉︻潔闂侀潧楠忕槐鏇㈠储閸楃偐鏀介柣鎰綑閻忋儳鈧娲﹂崜鐔兼偘椤斿槈鐔沸ч崶锔剧泿闂備礁鎼崐鍦偓绗涘泚澶愬閳╁啫寮挎繝鐢靛Т閹冲繘顢旈悩缁樼厵闁荤喐婢橀顓炩攽閳╁啯鍊愬┑锛勫厴閺佸倿骞嗚缁嬪牓姊婚崒姘偓鐑芥嚄閸洖绠犻柟鎹愵嚙閸氬綊鏌″搴″箹缂佺媭鍨堕弻銊╂偆閸屾稑顏�
开发学院数据库MSSQL Server 在您的数据库项目中应用测试驱动的开发 阅读

在您的数据库项目中应用测试驱动的开发

 2009-02-07 10:20:29 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鎯у⒔閹虫捇鈥旈崘顏佸亾閿濆簼绨绘い鎺嬪灪閵囧嫰骞囬姣挎捇鏌熸笟鍨妞ゎ偅绮撳畷鍗炍旈埀顒勭嵁婵犲嫮纾介柛灞捐壘閳ь剛鎳撻~婵嬪Ω閳轰胶鐤呯紓浣割儐椤戞瑩宕ョ€n喗鐓曟い鎰靛亝缁舵氨绱撻崘鈺傜婵﹤顭峰畷鎺戔枎閹搭厽袦婵犵數濮崑鎾绘⒑椤掆偓缁夌敻骞嗛悙鍝勭婵烇綆鍓欐俊鑲╃磼閹邦収娈滈柡灞糕偓鎰佸悑閹肩补鈧尙鏁栧┑鐐村灦閹稿摜绮旈悽绋课﹂柛鏇ㄥ灠閸愨偓濡炪倖鍔﹀鈧繛宀婁邯濮婅櫣绱掑Ο璇茶敿闂佺ǹ娴烽弫璇差嚕婵犳碍鏅插璺猴工瀹撳棝姊虹紒妯哄缂佷焦鎸冲畷鎴﹀箻鐠囧弶宓嶅銈嗘尰缁嬫垶绂嶉悙顒佸弿婵☆垳鍘ф禍楣冩倵濮樼偓瀚�闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鎯у⒔閹虫捇鈥旈崘顏佸亾閿濆簼绨奸柟鐧哥秮閺岋綁顢橀悙鎼闂侀潧妫欑敮鎺楋綖濠靛鏅查柛娑卞墮椤ユ艾鈹戞幊閸婃鎱ㄩ悜钘夌;婵炴垟鎳為崶顒佸仺缂佸瀵ч悗顒勬⒑閻熸澘鈷旂紒顕呭灦瀹曟垿骞囬悧鍫㈠幍缂傚倷鐒﹂敋缂佹う鍥ㄧ厓鐟滄粓宕滈敃鍌氱煑闁告劦鐓堝ḿ鏍煕濠靛棗鐝旂憸鏂跨暦閹偊妲炬繛瀵稿Т閵堢ǹ顫忛搹瑙勫珰闁肩⒈鍓涢澶愭⒑閻撳海绉虹紒鐘崇墵楠炲啯銈i崘鈺佲偓濠氭煢濡警妲奸柟鑺ユ礋濮婃椽妫冨☉杈€嗘繝纰樷偓铏枠鐎规洏鍨介幃浠嬪川婵炵偓瀚奸梺鑽ゅ枑閻熴儳鈧氨鍏樺畷顖濈疀濞戞瑧鍘遍梺缁樏壕顓熸櫠閻㈠憡鐓忛柛鈩冾殔閳ь剙婀辩紓鎾寸鐎n亜绐涙繝鐢靛Т鐎氼剟鐛崼銉︹拺缁绢厼鎳庨ˉ宥夋煙濞茶绨芥俊鍙夊姍瀵挳鎮㈤崫鍕ㄥ彏闂傚倸鍊搁崐椋庢濮橆兗缂氱憸宥囧弲闂侀潧鐗嗗ú鐘诲磻閹炬剚娼╂い鎰╁灩缁侇噣姊虹紒妯圭繁闁革綇缍侀悰顕€骞掗幊铏閸┾偓妞ゆ帒鍊绘稉宥夋煥濠靛棙顥犵紒鈾€鍋撻梻鍌氬€搁悧濠勭矙閹达箑姹叉繛鍡楃贩閻熸壋鍫柛顐犲灮閺嗩偊姊洪崫鍕効缂傚秳鐒﹂幈銊╁焵椤掑嫭鐓冮柟顖滃绾偓绻濋埀顒佹綇閵娧呭骄闂佸搫娲ㄩ崰鎾跺姬閳ь剙鈹戦鏂や緵闁告﹢绠栧畷銏ゆ偨閸涘ň鎷虹紓鍌欑劍閿氬┑顕嗙畵閺屾盯骞橀弶鎴濇懙闂佽鍠楄摫婵炵厧绻樻俊鎼佸Χ閸モ晝鏆伴梻鍌欑濠€杈╁垝椤栨粍鏆滈柣鎰摠濞呯姵绻涢幋鐐寸殤缁炬崘鍋愮槐鎾存媴鐠愵垳绱板┑鐐村絻椤曨參鍩€椤掑喚娼愭繛鍙夌墪閻g兘顢楅崘顏冪胺闂傚倷绀侀幉锟犲礉閺囥垹鐤柣妯款嚙缁€鍫熺節闂堟稓澧涚€规洖寮剁换娑㈠箣閻愩劎绱伴梺鍝勬濡鍩為幋锔藉亹閺夊牜鍋勯崢锟犳⒑鏉炴壆鍔嶉柣妤佺矌濡叉劙骞樼€涙ê顎撴繛瀵稿Т椤戝懘骞楅悽鍛娾拺闁革富鍘介崵鈧┑鐐茬湴閸婃繈骞冩ィ鍐╁€婚柦妯侯槺椤斿﹪姊虹憴鍕剹闁告ü绮欏畷鎾绘偨閸涘ň鎷洪梺鑽ゅ枑濠㈡﹢骞冩笟鈧弻锝夊箳閻愮數鏆ら梺璇″枟椤ㄥ﹪鐛弽銊﹀闁稿繐顦扮€氳棄鈹戦悙鑸靛涧缂佹彃娼″畷鏇㈠Χ婢跺﹤鎯為梺閫炲苯澧存慨濠冩そ楠炴牠鎮欏ù瀣壕闁哄稁鍘介崑瀣煟濡灝鍚圭€规挷绶氶悡顐﹀炊閵娧€濮囬梺鍝勬噺閹倿寮婚妸鈺傚亞闁稿本绋戦锟�濠电姷鏁告慨鐑藉极閸涘﹥鍙忛柣鎴f閺嬩線鏌涘☉姗堟敾闁告瑥绻橀弻锝夊箣閿濆棭妫勯梺鍝勵儎缁舵岸寮诲☉妯锋婵鐗婇弫楣冩⒑閸涘﹦鎳冪紒缁樺灴婵$敻宕熼姘鳖啋闂佸憡顨堥崑鐔哥閼测晝纾藉ù锝呮惈椤庡矂鏌涢妸銉у煟鐎殿喛顕ч埥澶愬閻樼數鏉搁梻鍌氬€搁悧濠勭矙閹烘鍊堕柛顐犲劜閸婄敻鏌i悢鍝勵暭闁哥喓鍋熺槐鎺旀嫚閹绘帗娈绘繝纰夌磿閺佽鐣烽悢纰辨晬婵﹢纭搁崯瀣⒑鐠囨煡鍙勬繛浣冲洤绠烘繝濠傜墛閸嬧晛鈹戦崒姘暈闁抽攱鍨归惀顏堫敇閻愭潙顎涘┑鐐插悑閸旀牜鎹㈠☉銏″殤妞ゆ巻鍋撻柡瀣閵囧嫰顢曢姀銏㈩唺缂備浇椴哥敮鎺曠亽闂佸吋绁撮弲婊堝吹瀹€鍕拻濞撴埃鍋撻柍褜鍓涢崑娑㈡嚐椤栨稒娅犳い鏃囧亹閺嗗棝鏌ㄥ┑鍡欏闁告柨鐏氶妵鍕晜閻e苯寮ㄩ梺璇″櫙缁绘繃淇婇懜闈涚窞閻庯綆鍓欑敮楣冩⒒娴gǹ顥忛柛瀣噽閹广垽宕橀鑲╋紱濡炪倕绻愰幊鎰不閸撗€鍋撻悷鏉款棌闁哥姵娲滈懞杈ㄧ節濮橆剛鐣鹃梺缁樻煥閸氬鍩涢幋锔藉€甸柛锔诲幖鏍¢梺闈涙閸熸挳寮婚妶澶婄闁肩⒈鍓欓悡鐔兼倵鐟欏嫭绀冪紒璇茬墦瀵偊宕橀鑲╁姦濡炪倖甯掔€氀囧焵椤掍焦顥堢€规洘锕㈤、娆撳床婢诡垰娲﹂悡鏇㈡煃閳轰礁鏋ゆ繛鍫燂耿閺岋綁鎮㈢粙鍨潚濠殿喖锕ュ浠嬪箖閳╁啯鍎熼柍鈺佸暞閻︼綁姊绘担铏瑰笡闁绘娲熸俊鍓佺矙鐠恒劍娈鹃梺缁樺灦宀h法寮ч埀顒勬⒑閹肩偛鍔€闁告劑鍔庨妶顕€姊婚崒娆戠獢婵炰匠鍕垫闊洦娲橀~鏇㈡煛閸ャ儱鐏╅柛灞诲妽閵囧嫯绠涢幘璺侯杸闂佹娊鏀遍崹鍧楀蓟閻斿吋鍤冮柍杞版缁爼姊洪崨濠冣拹妞ゃ劌锕濠氭晸閻樻彃绐涘銈嗘閺侇喗鎱ㄩ崶鈺冪=濞达絿枪閳ь剙婀遍弫顕€鎮㈡俊鎾虫川閳ь剟娼ч幗婊呭婵傜ǹ绾ч柛顐g☉婵¤偐绱掑Δ浣侯暡缂佺粯鐩幃鈩冩償閿濆浂鍟嬮梻浣虹《閺備線宕滃┑瀣闁告稑鐡ㄩ悡銉╂倵閿濆懐浠涚紓宥嗩殜濮婂宕掑顑藉亾瀹勬噴褰掑炊瑜滃ù鏍煏婵炵偓娅嗛柛濠傛健閺屻劑寮撮悙娴嬪亾閸濄儳涓嶉柡灞诲劜閻撴洟鏌曟径妯烘灈濠⒀屽櫍閺岋紕鈧絺鏅濈粣鏃堟煛瀹€鈧崰鏍х暦濠婂棭妲鹃柣銏╁灡閻╊垶寮婚敓鐘插窛妞ゆ棁妫勯埀顒佸姍閺岋紕浠︾拠鎻掝潎闂佽鍠撻崐婵嗙暦閹烘垟妲堟慨妤€妫旂槐锟�闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鎯у⒔閹虫捇鈥旈崘顏佸亾閿濆簼绨绘い鎺嬪灪閵囧嫰骞囬姣挎捇鏌熸笟鍨妞ゎ偅绮撳畷鍗炍旈埀顒勭嵁婵犲嫮纾介柛灞捐壘閳ь剛鎳撻~婵嬪Ω閳轰胶鐤呯紓浣割儐椤戞瑩宕ョ€n喗鐓曟い鎰靛亝缁舵氨绱撻崘鈺傜婵﹤顭峰畷鎺戔枎閹搭厽袦婵犵數濮崑鎾绘⒑椤掆偓缁夌敻骞嗛悙鍝勭婵烇綆鍓欐俊鑲╃磼閹邦収娈滈柡灞糕偓鎰佸悑閹肩补鈧尙鏁栧┑鐐村灦閹稿摜绮旈悽绋课﹂柛鏇ㄥ灠閸愨偓濡炪倖鍔﹀鈧繛宀婁邯濮婅櫣绱掑Ο璇茶敿闂佺ǹ娴烽弫璇差嚕婵犳碍鏅插璺猴工瀹撳棝姊虹紒妯哄缂佷焦鎸冲畷鎴﹀箻鐠囧弶宓嶅銈嗘尰缁嬫垶绂嶉悙顒佸弿婵☆垳鍘ф禍楣冩倵濮樼偓瀚�  闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚敐澶婄闁挎繂鎲涢幘缁樼厱闁靛牆鎳庨顓㈡煛鐏炶鈧繂鐣烽锕€唯闁挎棁濮ら惁搴♀攽閻愬樊鍤熷┑顕€娼ч~婵嬪Ω瑜庨~鏇㈡煙閹规劦鍤欑痪鎯у悑缁绘盯宕卞Ο铏圭懆闂佸憡锕槐鏇犳閹惧鐟归柛銉戝嫮褰梻浣规偠閸斿矂鎮ラ崗闂寸箚闁圭虎鍠栫粈鍐┿亜閺冨倸甯剁紒鎰洴濮婃椽宕崟鍨ч梺鎼炲妼缂嶅﹤鐣烽姀鐘嗘椽顢旈崨顓涘亾閸偒娈介柣鎰皺娴犮垽鏌涢弮鈧喊宥夊Φ閸曨垱鏅滈悹鍥皺娴犳悂鎮楃憴鍕┛缂佺粯绻堥悰顔芥償閵婏箑娈熼梺闈涳紡閸愩劌顩梻鍌氬€搁オ鎾磻閸曨個娲晝閳ь剛鍙呴梺鍝勭Р閸斿孩鏅堕敓鐘斥拻闁稿本鐟︾粊鐗堛亜閺囧棗鎳夐崑鎾诲垂椤愩垺璇為悗瑙勬礃缁捇骞冮姀锛勯檮濠㈣泛顑囩粙渚€姊绘担鐟板姢缂佺粯鍔曢敃銏℃綇閳轰緡妫滈梺绋跨箻濡法鎹㈤崱妯镐簻闁哄秲鍔庨。鏌ユ煙椤栨氨澧涢柕鍥у椤㈡洟濮€閵忋垹濮辨繝娈垮枛閿曘儱顪冮挊澶屾殾闁绘垹鐡旈弫鍥ㄧ箾閹寸偟鎳冮柣婵嬩憾濮婄粯鎷呴崨闈涚秺椤㈡牠宕ㄩ鍥ㄧ☉閳规垹鈧綆浜為悾楣冩⒑闁偛鑻晶顖炴煏閸パ冾伂缂佺姵鐩獮妯尖偓鍨偠閸嬫劖绻濈喊妯活潑闁搞劍澹嗛埀顒佺濠㈡﹢锝炶箛鎾佹椽顢旈崟顏嗙倞闂備礁鎲″ú锕傚礈濮樿泛绠柛妤冨亹閺€浠嬫煟閹邦厼绲荤紒鐙欏啰鏆嗛柨婵嗘噺閸嬨儲顨ラ悙鏉戠伌濠殿喒鍋撻梺缁橈供閸嬪懘寮埀顒€鈹戦悩鍨毄濠殿喖顕埀顒佸嚬閸o絽顕i崼鏇炵濞达絽鍘滈幏娲⒑閸涘﹦绠撻悗姘煎幖椤斿繐鈹戦崱蹇旀杸濡炪倖姊归崕鎶藉储閹绢喗鐓欐い鏃囶嚙瀹撳棗鈹戦敍鍕幋闁糕晪绻濆畷鎺懳旀担鍓蹭紲濠电姷鏁搁崑鐘诲箵椤忓棗绶ゅù鐘差儏缁犺銇勯幇鈺佲偓鏍汲濠婂牊鍋i弶鐐村椤掔喖鏌i弬鎸庮棦闁哄矉缍侀幃銏犵暋閹殿喚娉块梻浣姐€€閸嬫捇鏌ゆ慨鎰偓妤冨婵傚憡鐓曢悘鐐靛亾閻ㄦ垹鈧稒绻傝灃婵°倕锕g花鐑芥煕濡も偓閸熷潡鎮鹃悜鑺ュ亗閹兼惌鍠楅崓鐢告⒑閹稿海绠撻柟鍐茬У缁旂喖寮撮姀鈾€鎷洪梺鍛婄箓鐎氼剟寮冲▎鎾寸厽婵°倐鍋撴俊顐g〒閸掓帡宕奸妷銉ь槰闂佸磭鎳撻妵妯艰姳婵犳碍顥婃い鎰╁灪閹兼劖绻涚€电ǹ鍘撮挊婵嬫煥閺囨浜鹃梻鍥ь槹缁绘繃绻濋崒婊冣叡闂佷紮绲惧浠嬪蓟閿涘嫪娌悹鍥ㄥ絻椤牓姊虹€圭媭娼愰柛銊ョ仢閻g兘宕¢悙鈺傤潔闂佺懓鍚€缁€浣圭閻愵兛绻嗛柕鍫濆€告禍鎯ь渻閵堝骸寮ㄩ柛搴☆煼绡撳〒姘e亾闁哄本鐩幃鈺佺暦閸パ€鎷版繝鐢靛Л閸嬫挸銆掑锝呬壕濠殿喖锕ㄥ▍锝囨閹烘嚦鐔煎即閻旈浼岄梺璇″枤閸忔ɑ淇婇悿顖fХ闂佺ǹ顑嗛幐鎼侊綖濠靛鍋傞幖娣灮娴滃墽绱撻崒娆戣窗闁哥姵姘ㄩ崚鎺楊敍閻愬弬褔鏌ㄥ┑鍡╂Ц缂佲偓閸愵喗鐓冮柛婵嗗閺嗘瑦顨ラ悙鑼濞e洤锕幃娆擃敂閸曘劌浜鹃柡宥庡幖缁€澶愭煙鏉堝墽鐣辩痪鎯х秺閺岋繝宕堕妷銉т患闂佸憡鍨规繛鈧鐐寸墪鑿愭い鎺嗗亾濠碘€茬矙閺岋綁骞樼捄鐑樼亪闂佺粯鎼╅崑濠傜暦閹偊妾ㄩ梺绋块缁绘﹢寮诲☉銏犵睄闁逞屽墰閸掓帡骞樼拠鑼舵憰闂佸搫娲㈤崹褰掓倷婵犲嫭鍠愮€广儱顦介弫鍌涖亜閹捐泛袥闁稿鎸搁埢鎾诲垂椤旂晫浜舵繝鐢靛仜閸氬鎮烽妸鈺傚€堕柟鐑橆殕閳锋垿鏌涘☉姗堝姛缂佺姵鎹囬幃妤€顫濋悡搴♀拤濡炪們鍊曢崐鍦崲濠靛牆鏋堝璺虹灱閿涚喖姊虹粙娆惧剱闁搞劌鐏濋悾鐑藉箛閺夎法顓洪梺鎸庢磵閸嬫捇鏌涢妸銉モ偓鍦崲濞戞﹩鍟呮い鏃囧吹閸戝綊姊虹粙娆惧劀缂佺粯绻堝璇差吋婢跺﹣绱堕梺鍛婃处閸撴瑥鈻嶉敐鍥╃=濞达絽鎼牎闂佺粯顨堟繛鈧€殿喛顕ч鍏煎緞婵犲嫬骞愬┑鐐舵彧缁蹭粙骞夐垾鏂ユ灁闁哄被鍎查埛鎴犵磼鐎n偄顕滈柟鐧哥秮閺屾盯鎮╁畷鍥р拰閻庢鍠栭…宄邦嚕閹绢喖顫呴柣姗€娼ч埀顒傚仱閹嘲饪伴崘顎倝鏌ゆウ鍧楀摵缂佺粯绻傞~婵嬵敇閻樻彃绠ラ梻鍌欑閹诧繝宕归鐐茬9闁哄稁鍋€閸嬫挸顫濋悙顒€顏�
核心提示: 本文讨论: TDD 的优点 在数据库开发中应用单元测试 组合 T-SQL 与 .NET 兼容的语言 连接、测试条件和事务本文使用了以下技术:Visual Studio 2008, SQL Server目录 软件开发中的单元测试数据库单元测试定义连接管理 T-SQL 脚本修改生成的测试代码事务System.Tr

本文讨论:

TDD 的优点

在数据库开发中应用单元测试

组合 T-SQL 与 .NET 兼容的语言

连接、测试条件和事务

本文使用了以下技术:

Visual Studio 2008, SQL Server

在您的数据库项目中应用测试驱动的开发目录

软件开发中的单元测试

数据库单元测试

定义连接

管理 T-SQL 脚本

修改生成的测试代码

事务

System.Transactions

数据驱动的测试

自定义测试条件

定义测试条件

修改测试执行

展望

LMicrosoft 于 2006 年 11 月发布了 Visual Studio® Team System Database Edition,也称为 DBPro 或 Data Dude,它向产品生命周期方法中引入了数据库开发。DBPro 还引进了数据库单元测试设计器,使用它可以轻松地生成或编写面向 T-SQL 的单元,在开发前验证数据库对象。在本文中,我将深入阐述数据库单元测试,解释运用功能的方法,展示如何对您自己的数据库项目执行单元测试。

软件开发中的单元测试

在软件开发中,单元测试在确保质量水平和满足里程碑要求方面发挥着重要的作用。尽管很多开发人员均认可单元测试的重要性,但他们大都不会在产品周期结束时花费时间编写单元测试。为解决这一问题,测试驱动开发 (TDD) 方法应运而生。开发人员可以使用这种方法在实际运用功能前编写单个功能的单元测试。这样,开发人员就可以从外到内查看 API,尽可能简化设计。

如今,众多使用 C# 和 Visual Basic® 的开发人员均以 TDD 为指导,通过创建单元测试在尚未编写的类上试用方法,然后利用最少量的代码编译测试。如果编译后的测试失败,开发人员只需编写通过下一轮测试所必需的代码即可。每次迭代时均运行所有单元测试来验证没有出现回归错误。应用程序和单元测试代码随后签入源代码控件。每个单元测试针对所开发的方法运行两次。

为充分发挥作用,单元测试在其运行前必须设置自身的执行环境,以便稳定地验证其专门测试的代码。实施并验证测试后,将其签入源代码控件并在产品生命周期的其余部分使用,以确保按预期继续使用方法。

由于您需要提供稳定的测试环境以便在签入更改前验证代码,TDD 也因此在数据库开发中变得更具挑战性。为了让数据库单元测试成功运行,它必须具备合适的架构和数据。这意味着为运行测试,其他开发人员在创建单元测试时必须重用您所使用的环境。如果没有大量造价高昂的基础结构,这一点很难实现。幸运的是,DBPro 能让身处这一环境的开发人员遵循相同的单元测试流程。

从源代码控件中获得最新的测试和数据库项目,然后将新存储的过程加入数据库项目中。配置过程的输入和输出参数。

右键单击新存储的项目,然后选择“create unit tests”(创建单元测试)。将单元测试添加到现有的测试项目中。新测试类加入带有 T-SQL 的测试项目,T-SQL 将使用默认输入参数执行新存储的过程。

修改输入参数以包含预期值,然后添加测试条件以验证存储的过程是否返回了预期的结果。

运行测试。进行测试设置时,将数据库项目部署到本地的 SQL Server® 实例中,并执行数据生成计划以用预期的测试数据填充新部署的数据库。针对本地 SQL Server 实例执行测试,测试会失败(这是预期的结果,因为存储过程的逻辑尚未执行)。

执行存储过程的逻辑并再次运行测试。部署更新后的存储过程并通过测试。

运行所有测试来验证数据库,然后将存储的过程和单元测试代码签入源代码控件中。

DBPro 提供的功能包括:从现有的函数、存储的过程或触发器生成存根 T-SQL 单元测试;自动将数据库项目更新部署到沙箱实例中;使用数据生成计划在测试环境设置过程中生成数据;以及针对目标数据库执行 T-SQL 测试。尽管这些功能可一起使用,但并不要求必须这样做。例如,可以从头编写数据库单元测试,不必在每个测试运行前都生成数据。

数据库单元测试

数据库单元测试功能反映了在某种程度上相互竞争的两个目标:数据库开发人员希望使用熟悉的 T-SQL 脚本表达单元测试逻辑,并希望利用 C# 或 Visual Basic 这样面向 Microsoft® .NET Framework 的强大语言。通过在 T-SQL 代码中封装测试,然后在 C# 或 Visual Basic .NET 代码中托管 T-SQL,这两个竞争目标得以协调。单元测试设计器两者兼俱,可以修改其中的任何一个(或两个)来满足单独的需要。

如果您是设计自己的数据库测试架构,带有 Team System 的数据库单元测试提供或支持您想要的功能。您需要使用合适的上下文执行测试。例如,您需要在连接数据库时利用 Web 层将使用的凭据,或在执行测试前后能使用另一组凭据访问或修改基础表。能够在运行“测试组”中的每个测试之前和之后运行脚本来设置或重置数据库状态很有帮助,它等同于在测试前后能够运行单个测试的脚本来设置或重置数据库状态。

最后,您可以通过添加您数据库专用的条件来扩展可用测试条件组,利用事务控制对数据库状态的修改并使用数据推动多次运行具有不同输入和输出参数的测试。在某些情况下,您还需要其他代码才能使用这些功能。让我们来了解一下必需的步骤。

定义连接

如前所述,数据库测试工具可以不使用验证和设置数据库时的连接而使用其他连接执行测试,这一特点十分重要。为达此目标,Microsoft 创建了连接上下文这一概念,其中包含定义连接及其在执行测试时使用方式涉及的所有必要信息。基本连接上下文包括创建其他 ADO.NET 对象时所用的开放式连接、提供程序工厂 (msdn2.microsoft.com/system.data.common.dbproviderfactory) 及执行测试时使用的超时和 DbTransaction。

使用上下文类型可定义两个上下文实例:执行上下文和有权限的上下文,以支持测试设置、执行和验证。从名称上可以清楚地看出,执行上下文用于执行单元测试。它应代表使用数据库的应用程序配置。有权限的上下文用于验证单元测试并执行所有其他修改操作(包括部署和数据生成)。这两种上下文的定义存储在随附示例测试项目的 app.config 文件中一个专为数据库单元测试定义的区段内。

管理 T-SQL 脚本

从代码角度而言,数据库单元测试生成 C# 或 Visual Basic .NET 代码,它们在 Visual Studio 中构建单元测试框架。在单元测试框架中,使用一个类做为容纳测试的容器(每个测试定义为一种方法)。有多种机会可以在单元测试设计器中添加脚本以支持我曾阐述的脚本功能。除测试脚本外的其他所有脚本均使用有权限的上下文执行,因为它们是专门用于设置或验证测试环境的。脚本可能会执行一次或多次,具体视其添加的位置而定,因此让我们来了解一下脚本的细节和执行方式(请参见图 1)。

在您的数据库项目中应用测试驱动的开发Figure1共享的测试条件配置

脚本执行
测试初始化此 T-SQL 脚本用于在每次测试执行前设置测试环境。在类中的每次测试前均执行此脚本。
测试前设置执行具体测试前的设置您可使用测试前脚本对测试进行设置,或验证数据库是否已为测试做好准备。
测试测试本身。
测试后清理执行具体测试后的清理。测试后脚本供您恢复或验证数据库状态之用。
测试清理此 T-SQL 脚本用于在完成测试后恢复测试环境。每次测试后均执行此脚本。

现在,让我们看一下如何在数据库单元测试设计器中创建、访问或修改脚本。首先从“Test”(测试)菜单中选择“New”(新建)测试来创建数据库单元测试,然后选择数据库单元测试模板。完成向导中的步骤后,您将会看到提示您配置项目的对话框,现在将其取消(随后再加以讨论)。在解决方案资源管理器中,将新建一个名为 DatabaseUnitTest1 的文件,并打开相关的设计器供您创建第一个单元测试。

在图 2 中有三个区域值得注意。首先是左上方的下拉列表,您可以从中选择类中定义的通用脚本和测试。创建新的测试类时,会将名为 DatabaseTest1 的默认测试加入类中;您可使用“Rename”(重命名)按钮重命名此默认测试,并使用带有绿色加号的按钮添加其他测试。

在您的数据库项目中应用测试驱动的开发

图 2新单元测试

您可以在该按钮左侧的下拉列表中切换脚本。如果在左侧的下拉列表中选择了通用脚本,则可以编辑每次测试前后运行的测试初始化/清理脚本。另一方面,如果选择了“测试”,则可编辑前期/测试/后期脚本。

第三,您可使用“测试条件”面板访问基于 T-SQL 脚本执行结果工作的测试条件,以此验证结果是否与您的预期相符。单击超级链接“Click here to create”(单击此处创建)后,会启用下拉列表。通过下拉列表可以访问在您机器上注册的测试条件。

正如您从 UI 中看到的,所有脚本的模式均相同。您可以在任何位置定义脚本,也可以定义一个或多个测试条件,用它们验证脚本是否如期执行。编写单元测试代码时,脚本的验证至关重要。只执行一段代码是不够的,您还需要验证执行能否产生预期的效果。

此时,您可能会有所疑问:测试如何知晓连接哪个数据库以及我指出的有权限上下文的具体含义。答案是:您需要返回我先前让您取消的那个对话框。要返回该对话框,请选择“Test Project”(测试项目),然后转至“Test”(测试)|“Database Test Configuration”(数据库测试配置)(请参见图 3)。

在您的数据库项目中应用测试驱动的开发

图 3配置单元测试

在第一个下拉选项中,选择将用于执行测试的连接字符串。此连接字符串默认将可能使用 Windows® 身份验证,但您可以指定 SQL Server 身份验证。如果是这样,将输出并加密密码,然后将其存储在注册表内(测试执行期间会从注册表中检索密码)。

在第二个下拉列表中,指定修改和验证数据库状态使用的另一组凭据。例如,如果您想使用应用程序所用的凭据执行测试(比如,与数据库通信的 Web 层),您可能要使用另一组权限更高的凭据,以便验证数据库状态。所有其他脚本(包括部署和数据生成)均使用此连接字符串。如果您未指定连接字符串,则使用执行连接字符串。

在第三个下拉列表中,选择要在执行任何测试前部署的数据库。如果选择数据库项目(并选择了默认配置),将执行差异构建并将更改部署到执行连接字符串中指定的数据库内。如您未做出任何更改,这是一种快捷操作(归功于 DBPro SP1 中的更改),因为 MSBuild 和部署脚本会检测到没有做出任何更改。测试执行完毕后,您可通过单击“测试结果”工具窗口顶部的超级链接来查看数据库部署的结果。

在最后一个下拉列表中,选择在执行测试前应部署的数据生成计划。由于每次运行测试时均将执行此计划,因此您要配置少量的数据以减少实际开始执行花费的时间。

单击对话框中的“OK”(确定)后,信息将存储在测试项目的 app.config 文件中,如图 4 所示。至此,我已向您展示了 UI 包含的主要功能,现在开始深入地探讨实际代码。

在您的数据库项目中应用测试驱动的开发Figure4app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <configSections>
  <section name="DatabaseUnitTesting"
   type="Microsoft.VisualStudio.TeamSystem.Data
    .UnitTesting.Configuration.DatabaseUnitTestingSection,
    Microsoft.VisualStudio.TeamSystem.Data.UnitTesting,
    Version=9.0.0.0, Culture=neutral,
    PublicKeyToken=b03f5f7f11d50a3a" />
 </configSections>
 <DatabaseUnitTesting>
  <DataGeneration ClearDatabase="true" />
  <ExecutionContext
   Provider="System.Data.SqlClient"
   ConnectionString="Data Source=(local)sqlexpress
    ;Initial Catalog=Database1Sandbox;Integrated
    Security=True;Pooling=False"
   CommandTimeout="60"/>
  <PrivilegedContext
   Provider="System.Data.SqlClient"
   ConnectionString="Data Source=(local)sqlexpress
   ;Initial Catalog=Database1Sandbox;Integrated
   Security=True;Pooling=False" />
 </DatabaseUnitTesting>
</configuration>

修改生成的测试代码

单元测试 API 旨在让您能够更改或扩大测试工具的现用工作方式。Microsoft 预见到用户可能需要修改生成的代码、编写其自己的测试条件并用自己的实现取代默认测试服务。图 5 列出了构成数据库单元测试 API 的类。(详细信息另请参阅 msdn2.microsoft.com/microsoft.visualstudio.teamsystem.data.unittesting。)

在您的数据库项目中应用测试驱动的开发Figure5数据库单元测试 API 中的类

用途
TestCondition用于验证测试脚本的所有测试条件的基础类。组件的子类,支持设计时和运行时执行。在设计时,您使用在测试执行期间预期的值配置测试条件;在运行时,测试条件使用这些值验证实际的测试结果。
DatabaseTestClassVisual Studio 中所有数据库单元测试的基础类。此类的定义集成在 Visual Studio 设计环境中,负责在 InitializeComponent 中序列化代码。
ConnectionContext将连接封装到数据库。
DatabaseTestActions此类是一个组件,数据库单元测试的设计时表示。此类的实例托管包含一个测试的 DatabaseTestAction 实例。
ExecutionResult代表测试执行过程中一个批量执行的结果。
DatabaseTestAction此类是一个组件。它的实例包含脚本和测试条件集。
DatabaseTestService测试工具的默认实现。它实现数据库部署、数据生成、执行创建和有权限连接上下文以及测试执行。

通过 DatabaseTestClass 的静态属性访问测试服务的默认实例。如果您希望,可以用自己的实现替换默认实现,并覆盖上述任何实现。

有几个原因可能促使您修改生成的 Visual Basic 或 C# 代码。最常见的两种情形是在事务中执行单元测试或利用单元测试工具的数据驱动测试功能。

事务

在共享环境中执行的单元测试必须注意不要损坏执行环境;修改预期的数据或架构可以导致后续测试异常失败。数据库单元测试的作者尤其需要小心,因为极易出现此类错误。

这就是说,数据库单元测试的作者通过使用事务来避免修改基础数据库,这种方式对于任何数据库开发人员而言都是习以为常了。在此我将为您演示三种可以利用事务的方法。在执行的 T-SQL 内,使用此事务:

begin transaction
 -- Execute test
 -- We can clean up when the test passes successfully; otherwise, it
 -- is up to the engine to clean up the transaction when the
 -- connection is closed
rollback transaction

在 ADO.NET 事务中,执行图 6 中所示的代码。要使用 System.Transactions,请参看图 7。

在您的数据库项目中应用测试驱动的开发Figure7使用 System.Transactions

[TestMethod()]
public void SystemTransactionsExample()
{
 using (TransactionScope transactionScope =
  new System.Transactions.TransactionScope(
  TransactionScopeOption.Required))
 {
  base.ExecutionContext.Connection.EnlistTransaction(
   Transaction.Current);
  base.PrivilegedContext.Connection.EnlistTransaction(
   Transaction.Current);
  DatabaseTestActions testActions = this.SystemTransactionsExampleData;
  
  // Execute the pre-test script
  Trace.WriteLineIf((testActions.PretestAction != null),
   "Executing pre-test script...");
  ExecutionResult[] pretestResults = TestService.Execute(
   this.PrivilegedContext,
   this.PrivilegedContext,
   testActions.PretestAction);
  // Execute the test script
  Trace.WriteLineIf((testActions.TestAction != null),
   "Executing test script...");
  ExecutionResult[] testResults = TestService.Execute(
   this.ExecutionContext,
   this.PrivilegedContext,
   testActions.TestAction);
  // Execute the post-test script
  Trace.WriteLineIf((testActions.PosttestAction != null),
   "Executing post-test script...");
  ExecutionResult[] posttestResults = TestService.Execute(
   this.PrivilegedContext,
   this.PrivilegedContext,
   testActions.PosttestAction);
 }
}

在您的数据库项目中应用测试驱动的开发Figure6ADO.NET Transaction

[TestMethod()]
public void ADONetTransactionSample()
{
 base.ExecutionContext.Transaction =
  base.ExecutionContext.Connection.BeginTransaction();
 try
 {
  DatabaseTestActions testActions = this.ADONetTransactionSampleData;
  // Execute the test script
  Trace.WriteLineIf(
   (testActions.TestAction != null),
   "Executing test script...");
  ExecutionResult[] testResults = TestService.Execute(
   this.ExecutionContext,
   this.ExecutionContext,
   testActions.TestAction);
 }
 finally
 {
  base.ExecutionContext.Transaction.Rollback();
 }
}

在这三个选项内,如果您不使用前期/后期测试脚本设置数据库或验证测试结果,则 T-SQL 和 ADO.NET 事务发挥作用;切记,前期/后期脚本使用不同的连接执行,因此它们的修改无法复原。使用 System.Transactions 时,已经创建了有权限连接和执行连接,因此它们必须在实例化 TransactionScope 时创建的环境事务中登记,但所有更改均可回滚。

System.Transactions

由于使用环境事务(如先前的 System.Transactions 中所示)是最全面的解决方案,让我们进一步了解其中的细节。(您也可访问 msdn2.microsoft.com/ms172152 了解更多背景信息。)由于环境事务要跨两种不同的连接使用,而这两种连接可能具有不同的生存期,因此必须使用分布式事务处理协调器 (DTC) 服务来管理事务的生存期。这意味着为使用 System.Transactions,必须启动数据库的 DTC 服务才能通过测试。如果未启动服务,测试将失败,并出现类似如下所示的异常:

Test method TestSamples.DatabaseUnitTest1.SystemTransactionsExample threw exception:
System.Data.SqlClient.SqlException: MSDTC on server '(LOCAL)SQLEXPRESS' is unavailable.

修改每种方法以使用环境事务相当的乏味,您更有可能想要将所有的事务测试归组到一个类中,然后修改这个类以便为每个测试启动和停止事务(请参见图 8)。

在您的数据库项目中应用测试驱动的开发Figure8对每个测试使用事务

TransactionScope _ambientTransaction;
[TestInitialize()]
public void TestInitialize()
{
 // Create the transaction prior to the
 // connections being created. When the
 // connections are created they will
 // automatically enlist in the transaction
 ambientTransaction = new TransactionScope(
  TransactionScopeOption.Required);
 base.InitializeTest();
}
[TestCleanup()]
public void TestCleanup()
{
 // Since Complete() was not called, disposing
 // of the ambient transaction will cause all
 // changes to be rolled back
 ambientTransaction.Dispose();
 base.CleanupTest();
}

数据驱动的测试

您可使用 Visual Studio 2005 附带的测试工具多次运行单元测试,每次使用不同的输入参数组。此工具的使用说明文档可在以下两个位置找到:msdn2.microsoft.com/ms182527 中的“编写数据驱动的单元测试的代码”和msdn2.microsoft.com/ms243192 中的“演练:使用配置文件定义数据源”。

数据库单元测试 API 允许您为 Execute 方法提供零个或多个 DbParameter 实例,从而支持数据驱动的测试。为了解所提供的 DbParameter 实例的使用方法,将 SQL 测试脚本视为参数化的 SQL 可以直接引用 DbParameter 实例做为代码中的变量。图 9 显示了一个示例。

在您的数据库项目中应用测试驱动的开发Figure9提供 DbParameter 实例

[DataSource ("System.Data.SqlClient", "Data Source=(local)sqlexpress" +
 ";Initial Catalog=TestDB_DataDriven;Integrated Security=True",
 "DecisionAllocationTestData", DataAccessMethod.Sequential), TestMethod()]
public void DataDriveTestSample()
{
 // Setup and execute the test
 DatabaseTestAction test = new DatabaseTestAction();
 DbParameter customerID = CreateParameter(
  "@customerID", DbType.Int32,
  ParameterDirection.Input, "CustomerID");
 DbParameter offerID = CreateParameter(
  "@offerID", DbType.Int32,
  ParameterDirection.Input, "OfferID");
 DbParameter decision = CreateParameter(
  "@decision", DbType.StringFixedLength,
  ParameterDirection.Input, "Decision");
 DbParameter actualVault = CreateParameter(
  "@vault", DbType.Int32,
  ParameterDirection.Output);
 DbParameter actualBroker = CreateParameter(
  "@broker", DbType.Int32,
  ParameterDirection.Output);
 DbParameter actualMarket = CreateParameter(
  "@market", DbType.Int32,
  ParameterDirection.Output);
 test.SqlScript = @"
  declare @rc int
  execute @rc = AllocatePositionsByDecision
  @customerID,
  @offerID,
  @decision,
  @vault out,
  @broker out,
  @market out
 ";
 
 DatabaseTestClass.TestService.Execute(
  base.ExecutionContext, base.PrivilegedContext,
  test,customerID, offerID, decision, actualVault,
  actualBroker, actualMarket);
 // Verify the results
 int expectedVault = (int) base.TestContext.DataRow["ExpectedVault"];
 int expectedBroker = (int)base.TestContext.DataRow["ExpectedBroker"];
 int expectedMarket = (int)base.TestContext.DataRow["ExpectedMarket"];
 Assert.AreEqual(expectedVault, actualVault.Value, "Vault incorrect");
 Assert.AreEqual(expectedBroker, actualBroker.Value, "Broker incorrect");
 Assert.AreEqual(expectedMarket, actualMarket.Value, "Market incorrect");
}
private DbParameter CreateParameter(string paramName,
 DbType paramType, ParameterDirection paramDirection)
{
 return CreateParameter(paramName, paramType, paramDirection, null);
}
private DbParameter CreateParameter(string paramName,
 DbType paramType, ParameterDirection paramDirection,
 string dataColumnName)
{
 DbParameter newParameter =
  base.ExecutionContext.Provider.CreateParameter();
 newParameter.DbType = paramType;
 newParameter.Direction = paramDirection;
 newParameter.ParameterName = paramName;
 if (paramDirection == ParameterDirection.Input ||
  paramDirection == ParameterDirection.InputOutput)
 {
  newParameter.Value = base.TestContext.DataRow[dataColumnName];
 }
 return newParameter;
}

正如在图 9 中所看到的,测试方法配备了一个属性,它通知测试工具:希望针对从 DecisionAllocationTestData 表(位于本地 SQL Server Express 实例上的 TestDB_DataDriven 数据库中)检索到的每行数据调用一次。DatabaseTestAction 类代表测试中的一个操作,该测试包含要执行的测试脚本和评估结果的测试条件。您会注意到我创建了五个 DbParameter 实例,代表在测试脚本中引用的变量;请特别注意,这些参数有的是输出参数。CreateParameter 方法只是个小型帮助程序方法,用于创建实例并有选择地使用测试上下文中的数据填充实例。

您接下来看到的测试脚本定义为引用 DbParameter 实例,我将这些实例做为 Execute 方法的参数。如果您熟悉 ADO.NET 代码,您将看到这就是参数化的 SQL。

接着,我调用默认 TestService 实例的 Execute 方法,将 DbParameter 实例做为参数传递。执行后我会验证结果。验证时只需从 DataRow 检索预期值,然后将其与输出参数进行比较即可。

图 9 中的示例显示,可以不使用设计器,直接将数据库单元测试 API 设为目标。我使用从测试表中检索到的数据推动存储过程的执行;所测试的存储过程使用前三个输入值计算共享的数量,这些共享从不同位置检索以执行客户的决策。此计算仅取决于输入,过程很复杂,但输出很简单。因此,最好使用数据驱动格式编写此测试,而不是多次重复相同的测试,每次只是数据不同。

至此,我已阐述了几种不同的方法,供您修改或使用现有 API,从而充分利用事务或创建数据驱动的测试。可以使用几种方法扩展或修改数据库单元测试 API,例如添加更多的测试条件或创建自己的测试服务。因此,下面我们就来看看扩展框架的方式。

自定义测试条件

数据库单元测试功能附带六个测试条件,它们可用于验证单元测试的执行。尽管这些测试条件足以应对基本应用情况,很多用户还是很快发现他们需要验证的是一个结果集,而非单个值,因此我另行构建了两个测试条件。在 Team System 数据团队发布的一组 Visual Studio 2008 Power Tools 中,这两个测试条件和 LoggingTestService 是新增的内容。要下载这些功能强大的新工具,请参阅 go.microsoft.com/fwlink/?Linkid=107300。

第一个测试条件是 ChecksumCondition。在设计时,它生成预期结果的哈希。在执行时,实际的结果被混列并将结果值与预期结果相比较。其次是 ExpectedSchemaCondition,用于在设计时收集和存储预期的架构。在执行时,实际架构与预期架构相比较。

利用测试条件时,用户可创建数据库单元测试类并从可用条件列表中添加测试条件(将新测试类添加到项目时会自动添加对包含新条件的程序集的引用)。

用户可通过按配置属性中的椭圆访问测试条件的配置。结果对话框(如图 10 所示)允许用户指定并执行检索预期数据的查询;在此图中,显示的数据是由 DBPro Data 生成器生成的随机(无意义)数据。第一次显示时,测试中的 T-SQL 在文本框中提供。用户可随后修改测试代码以反映其实际想要的查询,然后单击“Retrieve”(检索)按钮根据服务器执行代码并显示结果。用户对结果感到满意后,便可单击“OK”(确定),测试条件会相应对自身进行配置。

在您的数据库项目中应用测试驱动的开发

图 10共享的测试条件配置

用户不必再与条件进行交互。对最终结果有所了解后,您可创建这些测试条件。开发先前的测试条件时,所关注代码的主体在设计时行为中。在下一章节中我将介绍要点;完整的代码位于 msdn.microsoft.com/msdnmag/code08.aspx。

定义测试条件

定义新的测试条件时,我发现最简单的方法是先定义条件的框架和注册,在开发实际的条件逻辑前,先验证框架是否正常工作。由于两种示例测试条件极为相似,我定义了一个通用基础类,如图 11 中所示。项目编译完毕后,我接着定义了注册 XML 文件,称为 UnitTesting.Samples.extensions.xml,如图 12 中所示。

在您的数据库项目中应用测试驱动的开发Figure12UnitTesting.Samples.extensions.xml

<?xml version="1.0" encoding="utf-8"?>
<extensions
 assembly="UnitTesting.Samples, Version=1.0.0.0,
  Culture=neutral, PublicKeyToken=382814fd17d42dea" version="1"
 xmlns="urn:Microsoft.VisualStudio.TeamSystem.Data.Extensions"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="urn:Microsoft.VisualStudio.TeamSystem
  .Data.Extensions
  Microsoft.VisualStudio.TeamSystem.Data.Extensions.xsd">
 <extension
  type="UnitTesting.Samples.TestConditions.ExpectedSchemaCondition"
  enabled="true"/>
 <extension type=" UnitTesting.Samples.TestConditions.ChecksumCondition"
  enabled="true"/>
</extensions>

在您的数据库项目中应用测试驱动的开发Figure11定义的常用基础类

namespace UnitTesting.Samples.TestConditions
{
 public abstract class DataSetTestCondition : TestCondition
 {
  //...
 }
}
namespace UnitTesting.Samples.TestConditions
{
 [DisplayName("Checksum")]
 public class ChecksumCondition : DataSetTestCondition
 {
  // ...
 }
}
namespace UnitTesting.Samples.TestConditions
{
 [DisplayName("Expected schema" )]
 public class ExpectedSchemaCondition : DataSetTestCondition
 {
  // ...
 }
}

我想将扩展文件直接添加到项目中,并将其设置为自动复制到输出目录,这样便于在开发和部署期间引用和使用。最后一步是设置自动部署,方法是将扩展文件复制到 DBPro 目录中,并将测试条件程序集安装到全局程序集缓存 (GAC) 中。通过生成后步骤或使用提升权限运行命令提示可达此目的。在我这个项目的生成后步骤中,我使用了以下脚本:

set TargetNameVar=$(TargetName)
set DevEnvDirVar=$(DevEnvDir)
set WindowsSDKVar=$(ProgramFiles)Microsoft SDKsWindowsV6.0A
copy /Y %TargetNameVar%.extensions.xml "%DevEnvDirVar%....DBPro*"
copy /Y %TargetNameVar%.pdb "%DevEnvDirVar%PublicAssemblies*"
"%WindowsSDKVar%Bingacutil" /if %TargetNameVar%.dll

如果想要将脚本作为单独的 .bat 文件运行,可修改前三行以从环境变量(而不是 MSBuild 变量)中检索值。

这一初始设置完成后,在测试条件下拉列表中您可看到两个新条件。我将 DisplayNameAttribute (msdn2.microsoft.com/system.componentmodel.displaynameattribute) 添加到了测试条件中,以便它们显示为 Checksum 和 Expectedschema。既然设置已完成,我便需要开发出测试条件的设计时体验,以便用户可以单击属性浏览器中的椭圆并配置条件实例。

如果您已在 Visual Studio 中设计过 Windows 窗体的组件,可能已经很熟悉 .NET Framework 中的 UITypeEditor。UITypeEditor (msdn2.microsoft.com/system.drawing.design.uitypeeditor) 和 EditorAttribute (msdn2.microsoft.com/system.componentmodel.editorattribute) 使组件开发人员可以设计自定义的编辑体验,并将其指配给(通过 EditorAttribute)在 IDE 中设计的组件属性。在本例中,我想塑造模式对话框这种体验,所以我按图 13 中所示定义了编辑器。因为两种测试条件的配置方式相同,所以我在基础类上定义属性:

在您的数据库项目中应用测试驱动的开发Figure13定义编辑器样式

namespace UnitTesting.Samples.TestConditions.Design
{
 class DataSetEditor : UITypeEditor
 {
  public override UITypeEditorEditStyle
   GetEditStyle(ITypeDescriptorContext context)
  {
   return UITypeEditorEditStyle.Modal;
  }
  
  public override object EditValue(
   ITypeDescriptorContext context,
   IServiceProvider provider, object value)
  {
   MessageBox.Show("DataSetEditor::EditValue");
   return value;
  }
 }
}
[DesignOnly(true)]
[Editor(
 typeof(UnitTesting.Samples.TestConditions.Design.DataSetEditor),
 typeof(System.Drawing.Design.UITypeEditor))]
public string Configuration
{
  get { return "Press to configure"; }
  set { }
}

在编译和部署测试条件时,我看到了“配置”属性,单击椭圆后会显示消息框。至此,已完成了所有需要与 Visual Studio 集成的代码,我可以集中精力处理测试条件本身的逻辑了。

首先我创建了一个窗体,它将通过刚建立的 DataSetEditor 显示出来,允许用户配置测试条件预期的数据集。此窗体(名为 DataSetSelectorDialog)以测试中的 T-SQL 为种子,并允许用户根据目标服务器执行 T-SQL,以在配置测试数据前预览数据。一旦用户对数据集感到满意,便可以单击对话框中的“OK”(确定),这表示测试条件应使用数据集并将控件返回编辑器。

接下来,我会修改编辑器,使其在用户单击“OK”(确定)后显示并处理窗体内容。通常,UI 编辑器返回应在所编辑属性中存储的值,但在本例中,我想要属性显示 "Press here to configure" 文本并将数据集返回给已编辑的测试条件。由于条件处理数据集的方式各不相同,我在基础类上定义了一种虚拟方法,让编辑器调用这个方法设置数据集实例,派生类会覆盖方法以添加其特定的实现。有了这些更改,EditValue 方法现在如图 14 中所示。在此,我检索 IUIService 以显示模式对话框;此服务将模式对话框的显示与 Visual Studio 窗口操作行为的其余部分集成。

在您的数据库项目中应用测试驱动的开发Figure14EditValue 方法

public override object EditValue(
ITypeDescriptorContext context, IServiceProvider provider, object value)
{
 IUIService uiService = (IUIService)provider
  .GetService(typeof(IUIService));
 DataSetTestCondition cond = context.Instance as DataSetTestCondition;
 if (uiService != null && cond != null)
 {
  DataSetSelectorDialog dsDialog = new DataSetSelectorDialog();
  if (cond.Site != null)
  {
   dsDialog.Text = string.Format(CultureInfo.CurrentCulture,
    "Configuration for {0}", cond.Site.Name);
  }
  dsDialog.Query = cond.GetTestScript();
  dsDialog.StartPosition = FormStartPosition.CenterParent;
  if (uiService.ShowDialog(dsDialog) == DialogResult.OK)
  {
   value = dsDialog.Query;
   if (cond != null)
   {
    cond.ConfigureExpectedDataSet(dsDialog.Result);
   }
  }
 }
 return value;
}

既然您已看到了测试条件的设计时行为,就让我们了解一下根据目标服务器执行测试脚本并将其结果收集到数据集时发生的情况。测试执行完毕后,创建 ExecutionResult 类的一个实例并用测试执行的相关信息(包括数据集)进行填充。测试工具随后遍历为验证执行结果而配置的所有测试条件,并调用每个测试条件的 Assert 方法来验证这些结果。在本例所示的测试条件中,它们检索结果数组中第一个 ExpectedResult 中的数据集,然后 ChecksumCondition 生成实际数据集的哈希并将其与设计过程中生成的哈希相比较。ExpectedSchemaCondition 反序列化代表预期数据表(及其列)的空数据集并将其与测试生成的数据集相比较。

修改测试执行

通过 DatabaseTestService 类(在 DatabaseTestClass 的静态 TestService 属性上注册)的实例访问连接字符串、数据库部署和测试执行的默认行为。DatabaseTestService 类的方法标记为虚拟,因此可以单独或全部覆盖这些方法以更改测试的执行方式。有时您可能想要修改默认行为,例如,您在使用自定义连接字符串加密/解密机制、修改部署数据库或生成数据的方式,或自动记录执行结果。

通过编写自己的测试服务类,然后用工具注册该测试服务以便其可在测试执行期间使用,这样就能实现每个(或多个)此类修改。在本例中,我将编写一个测试服务,它会使用数据集序列化在文件中记录测试执行结果。此服务可配置为记录零个结果、记录所有结果或仅记录失败的结果。这一简单的服务使在工具中查看测试执行的结果变得非常便捷。

首先定义类;我只需修改测试执行的方式,因此类如图 15 中所示。正如您所看到的,LoggingTestService 是 DatabaseTestService 类的子类,并覆盖 Execute 方法。接着需要添加结果记录。注意,Execute 的默认实现是执行测试脚本并评估测试条件;但这与我的需求不符,因为如果验证失败,我将不能访问执行结果。因此,为了达到我的目标,我将在调用基础 Execute 方法后评估分配的测试条件,然后相应记录结果。完成的 Execute 实现如图 16 中所示。

在您的数据库项目中应用测试驱动的开发Figure16执行实现

public override ExecutionResult[] Execute(
 ConnectionContext scriptExecutionContext,
 ConnectionContext privilegedExecutionContext,
 DatabaseTestAction action,
 params System.Data.Common.DbParameter[] sqlParameters)
{
 // We want to evaluate the conditions ourselves so that we
 // can log the results from every test's execution
 List<TestCondition> conditions = new List<TestCondition>();
 if (action != null)
 {
  conditions.AddRange(action.Conditions);
  action.Conditions.Clear();
 }
 // Execute the test
 ExecutionResult[] results = base.Execute(
  scriptExecutionContext,
  privilegedExecutionContext,
  action,
  sqlParameters);
 // Verify and log the results
 bool verificationFailed = false;
 try
 {
  foreach (TestCondition condition in conditions)
  {
   if (condition.Enabled)
   {
    condition.Assert(privilegedExecutionContext.Connection, results);
   }
  }
 }
 catch
 {
  verificationFailed = true;
  throw;
 }
 finally
 {
  if (results != null &&
   (Verbosity == LogVerbosity.AllResults ||
   (verificationFailed && Verbosity == LogVerbosity.OnlyFailure)))
  {
   // Serialize the dataset to an .xml file to be visualized
   string resultPrefix = Guid.NewGuid().ToString("N");
   for (int resultIndex = 0; resultIndex < results.Length;
    resultIndex++)
   {
    string dsFilePath = Path.GetFullPath(string.Format(
     CultureInfo.CurrentCulture,
     "{0}-{1}.xml", resultPrefix, resultIndex));
    ExecutionResult result = results[resultIndex];
    result.DataSet.WriteXml(dsFilePath,
     System.Data.XmlWriteMode.IgnoreSchema);
    Trace.TraceInformation("Log {0} {1}", resultIndex, dsFilePath);
   }
  }
 }
 return results;
}

在您的数据库项目中应用测试驱动的开发Figure15LoggingTestService 类

namespace UnitTesting.Samples
{
 public class LoggingTestService : DatabaseTestService
 {
  public override ExecutionResult[] Execute(
   ConnectionContext scriptExecutionContext,
   ConnectionContext privilegedExecutionContext,
   DatabaseTestAction action,
   params System.Data.Common.DbParameter[] sqlParameters)
  {
   // Execute the test
   ExecutionResult[] results = base.Execute(
    scriptExecutionContext,
    privilegedExecutionContext,
    action,
    sqlParameters);
   return results;
  }
 }
}

它仍使用基础 Execute 实现,但随后它使用操作的测试条件验证结果并将该执行的结果写入文件。我使用 GUID 生成一个独有的名称,因为这个方法在单一测试执行期间会被多次调用。另外,检查空结果也很重要,因为调用无操作的 Execute 很常见。

服务编译为程序集后,我仍需要进行注册以便能在测试执行过程中使用。在我的这个示例中,我要向程序集中的所有测试注册服务,但仅在测试失败时做记录,所以我将 IntializeAssembly 方法修改为如下所示:

[AssemblyInitialize()]
public static void IntializeAssembly(TestContext ctx)
{
 LoggingTestService testService = new LoggingTestService();
 testService.Verbosity = LoggingTestService.LogVerbosity.OnlyFailure;
 DatabaseTestClass.TestService = testService;
 // Setup the test database based on setting in the
 // configuration file
 DatabaseTestClass.TestService.DeployDatabaseProject();
 DatabaseTestClass.TestService.GenerateData();
}

现在,如果我运行的单元测试失败,日志文件的路径会输出到测试的运行目录并且文件路径将显示在该测试的结果窗口中。

为查看造成测试失败的结果,我创建了一个非常简单的 Windows 窗体应用程序(在附带的示例代码下载中),用于反序列化和显示数据集。结束前,我应提到 Visual Studio 测试工具在其自身的 AppDomain 中执行每个测试程序集。由于服务实例是通过静态属性注册的,所以服务注册的持续时间仅限 AppDomain 的生存期。这意味着您需要在每个测试程序集的初始化期间注册测试服务。另外,您还应看一下侧栏“Team System for Database Professionals:资源”,了解更改数据库架构的详细信息。

展望

我在本文中提及了 DBPro 的诸多功能,但掌握精髓的最佳途径是亲手实践。我建议您亲身体验产品,包括使用数据生成 API 和新的 Visual Studio 2008 PowerTools。如果您遇到任何问题或有任何疑问,请随时访问我们的论坛,网址为:forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=725&SiteID=1。此外,另请访问 Sachin Rekhi 的白皮书“使用 Team Edition for Database Professionals 的数据库单元测试”,网址为msdn2.microsoft.com/bb381703。

Tags:数据库 项目 应用

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接