2013年12月26日木曜日

Мэдлэгт дусал нэмэр болох үнэгүй номнууд

Хөгжүүлэгч, программ хангамжийн төсөл удирдаж буй хүмүүст хэрэг болох ном, санаа авахуйц URL-г доор жагсаав. Цаашид хэрэг болохуйц ном сурах бичиг, URL нэмж баяжуулах санаа байна. Одоохондоо өөрийн уншсан номнуудаас зарим нэг үнэгүй уншиж болох номны URL-г жагсаалаа.


1. About Agile Software Project Development & Management:

http://wwwis.win.tue.nl/2R690/doc/ScrumAndXpFromTheTrenchesonline07-31.pdf  in English

http://www.infoq.com/jp/minibooks/scrum-xp-from-the-trenches   in Japanese


XP, Scrum 2-г хослуулан хэрхэн амжилттай хөгжүүлэлт хийж болох талаар зохиогч өөрийн туршлагаас хуваалцсан гайхамшигтай ном!

2. About JavaScript (node.js) & functional programming and creating non blocking event loop

Зөвхөн node.js-н талаарх ном юм шиг боловч, гацалтгүй хурдан ажилагаатай програмыг хэрхэн бичих үү, ер нь яаж программыг хөгжүүлэх ёстой юм, программыг яаж алхам алхмаар сайжруулах вэ гэдэг талаар бичсэн маш гоё ном. Өөрийнхөө сайн мэддэг хэлээр унших боломжтой!
Ёстой мэдлэг ханхалсан ном доо.

http://www.nodebeginner.org/


3. About Agile Development and OOP, Deseign Patterns in Japanese:


http://objectclub.jp/
http://www.techscore.com/tech/DesignPattern/index.html/

Дараа жагсаалтыг улам баяжуулах болно. Бас үнэтэй боловч хэрэг болохуйц номны жагсаалтыг бичинэ гэж бодож байгаа.

4. Энд ч ёстой үнэгүй  ном ярайж байгаа даа!
Манай нэг инженерээс ирсэн URL байгаа юм.

http://www.efytimes.com/e1/creativenews.asp?edid=119319

За ингээд үнэгүй номын жагсаалтаа дуусгая. Интернэт юм хойно хүсэл байгаад хайвал үнэгүй ном зөндөө бий. Танд санал болгох ном байвал ...


Дараа жинхэнэ үнэ цэнэтэй номын жагсаалтыг тавина аа.

2013年11月11日月曜日

Бүтээмж

Японд ирээд удаагүй байсан юм даг. Энэ явдал одоогоос 7 орчим жилийн өмнө болжээ. 

Нэг удаа Японы асар том даатгалын компаний системийн хөгжүүлэлтэнд оролцох завшаан тохиолдсон юм. Хөгжүүлэлтийн эхний үе шатаас эхлэн оролцохоор болсон ба манай баг гэхэд л 20 орчим хүнээс бүрдэх нүсэр том төсөл байлаа.

Дэлгэцийн зохиомжийг багийн гишүүд бүгд хийхээр болов оо. Асуудлын гол нь хэрэгцээ шаардлагыг бүрэн тодорхойлж дуусаагүй байв. Дэлгэц бүр 100-500 оруулах талбар (textbox, text area, combobox г.м) - тай агаад талбар бүрийн хувьд тухайн талбарын нэр (論理名: logical name, 物理名: physical name), оруулах өгөгдлийн төрөл, өгөгдлийн хэмжээ гэх мэтийг нэг бүрчлэн Excel фаилд шивж оруулах ажил байлаа. Ганцхан удаа хийнэ гэвэл ч асуудал байсангүй. Төслийн явцад 4-5 удаа өгөгдлийн төрөл, талбарын нэр өөрчлөгдөх нь энүүхэнд. Үүгээр зогсохгүй энэ зохиомжоос гүйцэтгэл (программчлах) хийнэ. Гүйцэтгэл хийхэд дараах кодыг бичих ёстой байлаа.
  • Form Class - Java Bean гэгдэх класс (UserInfoForm.java)
  • Дэлгэцнээс оруулсан өгөгдлийг шалгах тохиргоо:  apache commons validator-н тохиргоо (user-info-validator.xml)
  • Бизнес логик буюу процессор класс (UserInfoProcessor.java)
Япончууд дуулгавартай, хичээнгүй юм болохоор юу ч бодолгүй өөрчлөгдөх бүрт залхуугүй засаад, дээрх үйлдлийг давтаад л ... Би 2 удаа нэлээд тэвчээр гарган хийж үзлээ. Гурав дахиас нь дахиад хэдэн ч удаа өөрчлөгдөө билээ. Ерөөсөө VBA ашиглаад макро биччихвэл зохиомж өөрчлөгдөх бүрт автоматаар кодыг үүсгэж болох юм гэж бодлоо.

VBA мэдэхгүй тул жаал интернэт ухаад хагас өдөр оролдсоны эцэст ямар ч байсан бүгдийг нь генерацладаг болголоо. Гэхдээ япон үсэгтэй болоод ирэхээр үсэг нь танигдахаа байчихаад байлаа. Фаилаас унших, фаил руу бичих үед үсгийн кодыг зааж өгөөгүйгээс болсон нь тэр. Манай төсөлд VBA-д нэвтэрхий япон байсан тул түүнээс үсгийн кодыг хэрхэн зааж өгөх талаар жишээ программ авч макрогоо заслаа. Ингэж би амьдралдаа анх удаа source code generator хийж билээ.

Үүнээс хойш төсөл өрнөхийн хэрээр олон ч хүн шинээр ирж, олон ч удаа зохиомж өөрчлөгдөж, олон ч шинэ дэлгэц нэмэгдсэн билээ. Тэр бүрт миний хийсэн генератор хэрэглэгдэн, маш их цаш хугацаа, хөдөлмөр хэмнэсэн дээ.

Бас нэгэн удаа Curl 4 дээр программ бичих хэрэг гараад Curl-г сурах хэрэг гарав. Долоо хоног оролдсоны эцэст ойр зуурын программ бичихтэйгээ боллоо. Бичсэн программаа тестлэх хэрэг гарч, арай туршлагатай нэгнээс яаж тестлэх талаар асуув. Тэгсэн debug хийж л тестлэдэг юм ярьлаа. Энэ арга хэтэрхий бүтээмж багатай, яг үнэнийг хэлэхэд тохиромжгүй тэнэг арга шиг санагдсан тул интернэтээр хайж үзлээ. 

Харамсалтай нь JUnit шиг CurlUnit гэж байх авч Curl 6 буюу түүнээс дээших хувилбарыг дэмждэг гэсэн байлаа. Өөрөө бичихээр шийдэж JUnit-н source-г нааш цааш нь гүйлгэж жаахан харж байгаад 2 цаг нухаад хийсэн нь нэг иймэрхүү юм болсон юм. Гар утсан дээрх зурагны огноог харвал 2010.09.17 нд хийж байжээ. Сайн харвал цаана нь curl дээр бичсэн код харагдах ажгуу.


Энэ жилийн нэг сард (өмнөх компанид), шахуу төлөвлөгөөтэй хэрнээ тест хамралт (test coverage) өндөр байх ёстой гэсэн сонин өндөр шаардлагатай төсөлд ажиллахаар болов. Бүгд амжихгүй тул нэн түрүүнд зайлшгүй шаардлагатай хэсгийн тест кодыг бичээд тестлэх шаардлагагүй хэсгийг орхичихов. Тэгтэл нэлээн асуудал босох янзтай. Шинэ хүүхдүүд даргад загнуулаад л … Амжуулах боломжгүй, хэт богино хугацаанд бүгдийг тестлэж амжсангүй гэж хэлж чадахгүй, хэцүү байгаа бололтой. Мэнчийтлээ улайсан харагдна. Дөнгөж эхний шат болохоор дараагийн шатанд бас л ингэж хэлүүлж таарах болохоор 1 цаг оролдоод тестлэгдээгүй үлдсэн бүх JavaBean классыг тестлэх тест код генерацладаг нэг tool хийгээд тавьчихлаа. Нөгөө мэнчийтлээ улайсан нөхөр баярлаад жигтэйхэн тасүкаримасү (тус боллоо) гээд л ...

Одоогийн компанид дөнгөж сургууль төгссөн гэлтгүй гайгүй сайн, өсөх ирээдүйтэй, залхуу нөхөр бий. Тэр нөхөр нэг удаа ингэж байна. Тест кодонд ашиглаж байгаа өгөгдлийг гараар бэлтгэх нь их цаг хугацаа, хөдөлмөр зараад байна. Би интернэтээс хайж байгаад мэдээллийн сангаас одоо байгаа өгөгдлийг түүж аваад бэлдэц (fixture-тест өгөгдөл) хийдэг helper биччихлээ гэж байна. Бүх тест кодонд ашиглагддаггүй юм гэхэд автоматаар генерацласан өгөгдлийг засаад ашиглах нь цаг хугацаа хийгээд хөдөлмөр ихэд хөнгөвчлөх тул зөв зүйтэй санаачлага юм.

Энэ мэтээр багт нэг ийм хүн байхад бүтээмж хэд дахин илүү хурдан болдог юм. Эндээс миний юу өгүүлэх гэснийг ойлгосон байх гэж найдна.

Инноваци хэрхэн мэндэлдэг вэ?

Уг нь объект хандлагат программчлалын талаар бичье гэж төлөвлөж байсан авч энэ удаад  "Монголын Програм Хангамж Үйлдвэрлэлийн Салбарын Анхдугаар Чуулган" болж буйтай холбогдуулан инновацийн талаар бичмээр санагдлаа.

Японы NHK телевизийн боловсролын сувгаар даваа гариг бүрт гарах Super Presentation бол миний үзэх дуртай нэвтрүүлгийн нэг юм. Сар орчмын өмнө юм даг. Нэгэн дугаараар ахлах сургуулийн сурагч хэрхэн нойр булчирхайн хорт хавдарыг хямдхан, өндөр нарийвчлалтай, хурдан оношлох аргыг нээсэн талаар илтгэл тавьж байна.

Энэ явдал ойр дотныхоо хүнийг нойр булчирхайн хорт хавдраар алдсан явдлаас үүджээ. Харамсал, уй гашууд автсан тэр дотных нь хүнийг аваад явсан аюулт өвчний талаар интернэтээр (google, wikipedia) судлаад ихэд цочирджээ.

Юу вэ гэвэл: Нойр булчирхайн хорт хавдарын 85% нь эцсийн шатандаа илрэх агаад амьд үлдэх магадлал 2%-тай байдаг юм байна. Түүнээс гадна нойр булчирхайн хорт хавдарын 30% нь илэрдэггүй бөгөөд нэг удаагийн оношлогооний өртөг хэт өндөр-800$ байдгийг олж мэджээ. Оношлогооны энэ арга 60 жилийн турш огт өөрчлөгдөөгүйгээр барахгүй өөрийнх нь ааваас ч урт настай гэж хэлээд танхимд цугласан олныг инээлгээд авав.

Ингээд тэр илүү дэвшилтэт сайн арга олохоор шийджээ. 
  • Хямд, маш амарханаар хийж болох.
  • Илэрц сайтай, алдаа багатай.
Цааш нь 60 жилийн турш оношлогооны энэ арга сайжраагүй нь цусны шинжилгээгээр хүний биед байх олон мянган уургаас зөвхөн нэг төрлийн уургийн өчүүхэн бага өөрчлөлтийг илрүүлэх нь маш хэцүү байдгаас гэдгийг олж мэджээ.

Нойр булчирхайн хорт хавдраар өвчлөгсдөд 8000 төрлийн уургийн өөрчлөлт ажиглагддаг. Эдгээрээс био-заагч (bio-marker) болж болох зүйлийг хайж эхлэжээ. Яаж гэвэл, энэ өвчний эхний шатанд цусанд хамгийн ихээр агуулагддаг уургийг хайж олох. Дөрвөн мянган уургийг нэг бүрчлэн судласаар  арай хийж био-маркераар сонгох боломжтой mesothelin гэдэг уургийг олж илрүүлжээ. Энэ уураг угаас хүний биед байх боловч, нойр булчирхай, өндгөвч, уушигны хорт хавдрын эхний шатанд ихээхэн өсдөг байна. Иймээс энэ уургийн тоо хэмжээний өсөлтийг хэмжиж чадвал өвчний эхний шатанд, бараг 100% амьд үлдэх магадлалтай үед нь оношлох боломжтой ажээ.

Нэг өдөр тэр биологийн хичээл дээр сууж байгаад санамсаргүйгээр амархан хэмжих арга бодож олжээ. Энэ санаа сэтгүүл дээрээс нүүрс төрөгчийн нано хоолойн талаар үзэж байх үед төржээ. Хичээлээс илүү нано хоолойд хамаг анхаарлаа төвлөрүүлсөн яг тэр мөчид багш дархлааны систем (эсрэг биет)-ийн  талаар хичээл зааж байлаа. Эсрэг биет нь уургийн нэгдэлд онцгой урвал үзүүлдэг молекуль юм. 

Түүнд ийм нэгэн санаа төржээ. Нано хоолойг, эсрэг биеттэй хослуулбал, өөрөөр хэлбэл нано хоолойд байршуулсан эсрэг биетийн тусламжтайгаар зорилтот уургийн хэмжээг тодорхойлох боломжтой юм. Тухайлбал нано хоолойн цахилгаан дамжуулалтын өөрчлөлтөөр уургийн хэмжээг олох мэргэн санаа төржээ.

Гол бэрхшээл нь нано хоолой хэт хэврэг явдал байлаа. Гэхдээ үүнийг цаасанд нэвчүүлчихвэл болох нь тэр. Өөрөөр хэлбэл оношлогооны цаас (индикатор буюу илтгүүр) бэлдэх явдал гэж сэтгэжээ. 

Илтгүүр бэлтгэх нь маш хялбар. Усанд нано хоолойг эсрэг биеттэй хольж хутгаад, энэ бэлдмэлдээ цаасаа дүрж нэвчүүлээд хатаагаад л болоо.

Түүний нээсэн аргаар 800$-н өртөгтэй оношлогоо ердөө 3 цент хүртлээ буурчээ. Энээ хүртэлх зам дардан байгаагүй бөгөөд түүнд лаборатори хэрэгтэй болжээ. Ингээд тэрээр судлагааны төсөл бичээд 199 профессор руу захиа явуулсан боловч хэн ч түүнийг дэмжсэнгүй. Эцсийн эцэст нэгэн профессороос (200 дахь нь) нааштай хариу сонсон, 3 сарын дараа түүнтэй уулзахаар болов.

Гурван сарын дараа профессортой уулзаж, судлагааныхаа талаар танилцуулж эхэлтэл профессор 20 орчим судлаач эрдэмтдийг танхимд дуудаж, цугласан олон түүнийг элдэв янзын асуултаар булж гарав. Түүнийг унагаах гэсэн бүх оролдлогыг амжилттай няцааж чадсанаар арай хийж лабораторит ажиллах эрх олж авчээ.

Ингээд судлагааны ажлаа эхлүүлж, удалгүй түүний төлөвлөгөө бүрэн төгс биш болохыг олж мэдэв. Гэлээ ч шантралгүй шамдсаны хүчээр 7 сарын дараа 5 минутын дотор оношлох чадвартай илтгүүр гаргаж авч чаджээ.

Энэ оношлогооны хамгийн гайхамшигтай нь 6 арваны турш ноёрхсон оношлогооноос даруй 168 дахин хурдан, дор хаяж 26000 дахин хямд, нарийвчлал 400 гаруй дахин өндөр юм. 

Бас оношлогооны нарийвчлал 100% руу дөхүү, амьд үлдэх магадлал бараг 100% байх эхний шатанд нь илрүүлэх боломжтой. Иймээс энэ өвчнөөр шаналагсад ойрын 2-5 жилийн дотор амьд үлдэх магадлал 5.5%-аас бараг 100% хүртэл өсөх боломжтой. Нойр булчирхайгаас гадна өндгөвч, уушигны хорт хавдарыг ч оношилж болно. Цаашилбал эсрэг биетийн төрлийг сольж өөрчилсөнөөр бусад төрлийн өвчнийг оношлох боломжтой гэж нүд нь цогтойгоор гялалзах цовоо сэргэлэн жаал өгүүлж байх юм.

Түүний илтгэх чадварыг гайхаад баршгүй. Жинхэнэ эхийг эндээс үзвэл илүү их сэтгэл хөдлөм, бас хөгжилтэй байх болов уу. 

Яагаад энэ сэдвээр бичих болсоноо одоо тайлья. Ахлах сургуулиа ч төгсөж амжаагүй түүний хийж бүтээснийг харвал аливаа зүйлийг хийхэд хүсэл эрмэлзэл, уйгагүй оролдлого чухлаас гадна зөв сэдэл, сэтгэлгээний зөв хандлага, бүтээлч сэтгэлгээ чухал ажээ. Бүтээлч сэтгэлгээ байгаагүй бол ийм богино хугацаанд, 60 жил эрдэмтдийн хийж чадаагүй зүйлийг хийж чадаа ч уу, үгүй ч үү?

Хөгжүүлэгчийн ажил ч гэсэн бүтээлч сэтгэлгээ ихээр шаарддаг.
Залхуу инженерүүдээс шинэлэг санаа байнга төрдөг. Тэд залхуу учраас ажлаа хөнгөвчлөхийн тулд үргэлж янз бүрийн багаж хэрэгсэл зохиодог. Хүний оролцоог багасгасан шинэ оролдлого, шинэ сэдлийн ачаар өнөөдөр энэ салбарт өдөр бүр шинэ технологи, инноваци мэндэлж байна. 

Та ч гэсэн тэдний нэг байх боломж дүүрэн бий юм шүү. Энэ салбарын хөгжлийн хурдасгуур нь олон бүтээлч сэтгэлгээний үр дүн гэж би хувьдаа боддог юм. Тун удахгүй Монголоос минь дэлхийн хэмжээнд хүлээн зөвшөөрөгдсөн олон сайхан инноваци гарна гэж итгэж найдаж явдаг. 

Монголын бүтээлч сэтгэгчид олон болох болтугай!



2013年10月24日木曜日

Behavior Driven Development

Өмнөх бичлэгүүдэд TDD-н талаар хэд хэдэн удаа дурдсан билээ. Энэ удаа Behavior Driven Development (BDD)-н талаар цухас дурдъя.

BDD нь TDD-тэй зарчмын хувьд адилхан зүйл бөгөөд TDD-г хэрэгжүүлэх нэг арга зүй юм. TDD гэсэн нэг олонлог байна гэж үзвэл BDD нь түүний доторх нэг дэд олонлог мэт. Өөрөөр хэлбэл BDD нь TDD-н нэг тухайн тохиолдол болой. 

TDD нь хийх гэж буй программыг хөгжүүлэгчийн өнцгөөс (low level: software requirement) хардаг бол BDD бизнес хэрэгцээ талаас (high level: user requirement) нь хардаг. Ийм учраас BDD нь системийн шинжээч (эсвэл хэрэглэгч) болон хөгжүүлэгч хоёрын хоорондох ойлголцлын зөрүүг арилгах, нийтлэг нэг хэлээр (ubiquitous language) ярих боломжийг өгдөг*. 


* Программ хөгжүүлэлтийн нэг том асуудал бол төсөлд оролцогч талуудын ойлголцлын зөрүү байдаг. Оролцогч талууд нэгдсэн нэг ойлголттой болох, ойлголцлын зөрүүг арилгах үүднээс нийтлэг хэллэг (ubiquitous language)-тэй болох нь чухал. Энэ талаар Domain Driven Design гэдэг аргачлал буй. BDD-д Domain Driven Design-ы нийтлэг хэллэг гэдэг ойлголтыг оруулсанаар том дэвшил гарсан гэж би хувьдаа үздэг.

BDD нь
  • TDD-г хэрхэн зөв хэрэгжүүлэх вэ?
  • TDD-н талаарх буруу ойлголтыг яаж залруулах вэ?
  • TDD-г хэрхэн хялбархан ойлгуулах вэ? 
гэдэг эрэл хайгуулын үр дүнд буй болсон аргачлал юм.

Туршлага багатай хүмүүст TDD-г хэрэгжүүлэх явцад 
дараах бэрхшээлүүд гардаг байна:
  • Юуг тестлэх ёстой юм бэ? 
          Чухам алийг нь тестлээд, алийг нь теслтэх шаардлагагүй вэ? 
  • Тестийг хаанаас эхлэх вэ?
  • Тестийг хэр хэмжээгээр хийх вэ?
          Хэдийд тестийг дууссан гэж үзэх вэ?
  • Тест методыг юу гэж нэрлэвэл зохимжтой вэ?
  • Тест амжилтгүй болсон шалтгааныг хэрхэн олох вэ?

Юуг тестлэх ёстой юм бэ?

BDD-д шалтгаан үр дагаварын холбоосыг чадамгай ашиглаж шийдэх асуудлыг хялбархан томъёолдог. Одоо байгаа төлөвөөс дараагийн төлөвт ямар шалтгааны улмаас шилжиж болох вэ? Өөрөөр хэлбэл бодлогын даалгаврыг (программын спекийгшалтгаан үр дагаварын холбоос байдлаар бичих, эсвэл одоо байгаа спекийг энэ хэлбэрт хялбархан хувиргасанаар программын спек өөрөө тест спек болж хувирч чадна. Ингэснээр юуг тестлэх вэ гэдэг асуудал тодорхой болж байгаа юм.

Яг л бодлого бодож буй мэт. Ялгаатай нь хариуг нь урьдаас мэдэх агаад түүнийгээ системийн гаралт (behavior) ийм байх ёстой гэсэн байдлаар томъёолдог.

Given: Анхны буюу одоогийн төлөвт байхад (өгөгдсөн нь:)
When Оролт. Ямар нэгэн үзэгдэл өрнөж, тодорхой нөхцөл биелэхэд (гаднаас нөлөөлөхөд)
Then: Гаралт. Ямар хариу үйлдэл (behavior) хийх вэ?

Тодорхой жишээ авъя.
    Scenario: ATM-с бэлэн мөнгө гаргах
         Given: Дансны үлдэгдэл хүрэлцээтэй, 
         And: карт хүчинтэй байгаад,
         And: ATM-д хангалттай бэлэн мөнгө байгаа бол
         When: Хэрэглэгч бэлэн мөнгө авах хүсэлт гаргавал
        Then: дансны үлдэгдлээс [хүссэн мөнгөний хэмжээгээр] хасалт хийж чаддаг байх,
        And: бэлэн мөнгө АТМ-ийн мөнгөний амнаас гарч ирж байх,
        And: залгиураас карт буцаж гарч ирдэг байх ёстой.

Дээрх спекийг тест код болгож хөрвүүлбэл иймэрхүү болох ажээ.

public class ATMFunctionalTest {
    private ATM atm;

    @Given("Дансны үлдэгдэл (@remain) хүрэлцээтэй,")
    @And("карт хүчинтэй байгааd (@isValid),")
    @And("ATM-д хангалттай бэлэн мөнгө (@amountOfMoneyATMHas) байгаа бол")
    public void anATM(double remain, boolean isValid, double amountOfMoneyATMHas) {
        atm = new ATM(remain, isValid, amountOfMoneyATMHas);
    }

    @When("Хэрэглэгч бэлэн мөнгө (@requestedCashAmount) авах хүсэлт гаргавал ")
    public void cashRequested(double requestedCashAmount) {
        assertThat(atm.getAmountOfMoneyATMHas(),                      
                        equalOrGreaterThan(requestedCashAmount));
        assertThat(atm.getCashRemain(), equalOrGreaterThan(requestedCashAmount));
        assertThat(atm.isCardValid(), equalTo(true));
        atm.withdraw(requestedCashAmount);
    }

    @Then("дансны үлдэгдлээс хасалт хийж чаддаг байх (@cashRemain), ")
    @And("бэлэн мөнгө АТМ-ийн мөнгөний амнаас гарч ирж байх (@isCashDispensed),")
    @And("залгиураас карт буцаж гарч ирдэг (@isCardReturned) байх ёстой.")
    public void theCashRemainIs(double cashRemainboolean isCashDispensed, 
                                      boolean isCardReturned) {
        assertThat(atm.getCashRemain(), equalTo(cashRemain));
        assertThat(atm.isCashDispensed(), equalTo(isCashDispensed));
        assertThat(!atm.isCardInserted(), equalTo(isCardReturned));
    }
}


Тестийг хаанаас эхлэх вэ?

Хэрэглэгчийн хувьд хамгийн хэрэгцээтэй байгаа behavior (зан үйл: системийн ажиллагаа, гадны нөлөөлөлд үзүүлэх хариу үйлдэл) аль вэ? гэдэгт анхаарлаа тавьбал хандах зүг өөрөө тогтно (kkkk). 

Agile хөгжүүлэгчид ямагт хийх ажлаа эрэмбэлээд хэрэглэгчид хамгийн чухал, эсвэл хамгийн эрсдэлтэй гэсэн зүйлээс эхлэн гүйцэтгэхийг эрмэлздэг.



Тестийг хэр хэмжээгээр хийх вэ?

Программын спекээс (behavior or story*) ургаж гарсан сценарь (scenario) болгоныг тестлэж дуусаад, кодын давхцал байхгүй болсон бол дууссан гэж үзэж болох юм. Энгийнээр хэлбэл behavior-г дэлгэрүүлээд scenario (тухайн тохиолдол)-уудад задлаад тухайн scenario бүрийг тестлээд дуусахад л тест дуусна гэсэн үг юм.

* Agile хөгжүүлэгчид нүсэр том шинжилгээний бичиг баримтын оронд User Story хэмээх хялбар картыг (хэдхэн (1-3) өгүүлбэрт багтах ердөө л нэг хуудас) ашигладаг. Программын шаардлагыг жижиг story (behavior)-нуудад  хуваагаад хамгийн чухлаас нь эхлээд тухайн story дээр дурдагдсан нөхцөл шаардлагыг хангах хүртэл гүйцэтгэл хийнэ.


Тест методыг юу гэж нэрлэвэл зохимжтой вэ?

Тест методын нэрийг оновчтой зөв өгсөнөөр (бүтэн өгүүлбэрээр) яг тестлэх гэж буй зүйлдээ анхаарлаа төвлөрүүлэхээс гадна тест амжилтгүй болсон үед шалтгааныг хурдан мэдэх боломжтой болдог.

JUnit* мэтийн тест framework-г ашиглах үед тест методын нэр тест гэсэн үгээр эхлэдэг. Үүнээс болоод тестийн тайлан энгийн хүнд ойлгомж муутай болдог тал бий. Үүнийг agiledox гэдэг tool-ээр илүү хүний хэлэнд ойртуулж өгсөнийг анзаарсан Dan North (BDD-г үндэслэгч) методын нэрийг test-р биш should-р эхлэх ёстой гэсэн санаа гаргажээ. Нээрээ хэрэглэгчийн өнцгөөс харвал should-ингэх ёстой, ийм байх ёстой гэсэн спекийг шалгаж байгаа гэдгээ бүрэн ухамсарласнаар илүү зөв тест хийгдэх ажээ. Тестийн тайлан ч жирийн хүнд ч ойлгомжтой болж ирдэг байна.

*Cүүлийн хувилбаруудад annotation(@Test) ашиглаж болдог болсон тул тестийн нэр дурын байж болно.


Тест амжилтгүй болсон шалтгааныг хэрхэн олох вэ?

Dan North тест амжилтгүй болсон шалтгааныг хурдан олж чадахгүй байгаа нь хүний хэлэнд ойр, жирийн хүнд ч ойлгогдох тайлан байхгүйгээс болж байна гэж үзжээ.

Тестийн үр дүн :

Given Дансны үлдэгдэл (100) хүрэлцээтэй,
And карт хүчинтэй байгааd (true),
And ATM-д хангалттай бэлэн мөнгө (10000000) байгаа бол
When Хэрэглэгч бэлэн мөнгө (10) авах хүсэлт гаргавал 
Then дансны үлдэгдлээс хасалт хийж чаддаг байх (90),
And бэлэн мөнгө АТМ-ийн мөнгөний амнаас гарч ирж байх (true),
And залгиураас карт буцаж гарч ирдэг (true) байх ёстой.

Амжилттай болсон тест ногооноор амжилтгүй болсон тест улаанаар гарна.


Төгсгөлд нь

BDD нь TDD-н гүйцээлт юм шиг санагдлаа. TDD-д тестийг эхлэж бичээд, дараа нь гүйцэтгэнэ (жинхэнэ кодыг бичнэ) гэдэг ерөнхий зарчмыг зааж өгсөнөөс хэрхэн гэдгийг жаахан бүрхэг орхигдуулжээ. Харин BDD нь TDD-г хэрхэн амжилттай хэрэгжүүлэх вэ гэдэг талаас нь авч үзсэнээр туршлагагүй программчлагчдыг ойлгомжгүй байдлаас гаргасан юм болов уу?

Энэ арга нь хэрэглэгчийн хүсэлт хөгжүүлэлтийн шат бүрт гээгдэж, гуйвж ирэх, программын спекийг буруу ойлгохоос сэргийлэх сайн арга юм. Ялангуяа та "шинжилгээ-зохиомж-гүйцэтгэл-тест" гэсэн дарааллаар хөгжүүлж буй бол шинжилгээ-гүйцэтгэл хоёрын хооронд ямар нэгэн байдлаар зөрүү гарахтай бишгүй таарсан буй за.

Программмчлагч хэрхэн гоё код бичих вэ? Мэдээллийн сангийн тэр хүснэгтээс өгөгдлийг ингэж аваад тэгж дэлгэцэнд гаргаад гээд хэт системийн далд хэсгийг бодсоор (явцуурсаар) хэрэглэгчийн үндсэн хэрэгцээг мартах, дутуу ойлгох, жинхэнэ асуудлаас хөндийрч холдох явдал гардаг. Харин хэрэглэгчид гадны өдөөлтөнд систем ямар хариу үзүүлэх вэ гэдэг л чухал байдаг. Энэ 2 өөр ертөнцийн хүмүүсийг холбож байгаа бас нэгэн гүүр нь  BDD юм. Энэ утгаар BDD-г Spec Driven Development гэж бас нэрлэдэг.

Ингэж хэрэглэгчийн хэрэгцээнээс (system specification) шууд тест код гаргаж авч хөгжүүлсэнээр дээрх асуудлыг шийдэхээс гадна тест код их ойлгомтой болдог ажээ.

Дашрамд хэлэхэд одоо хөгжүүлж буй системд Rails ашиглаж буй бөгөөд rails-н built-in testing framework (minitest) ашиглаж буй. Өнөөдрөөс эхлээд RSpec-Capybara-Selenium ашиглаж илүү хялбархан тест хийх аргыг манай нэг инженер судлаж эхлэлээ (Тамир аа. Чи шүү!). Энд дурдсан RSpec бол BDD хөгжүүлэлтэд зориулсан testing framework юм.

Та BDD-н талаар илүү ихийг мэдэхийг хүсвэл:
  • http://dannorth.net/introducing-bdd/
  • http://d.hatena.ne.jp/digitalsoul/20090819/1250686015
  • http://blog.mattwynne.net/2012/11/20/tdd-vs-bdd/
  • http://ericlefevre.net/wordpress/2008/06/28/behavior-driven-development-vs-test-driven-requirements/
  • http://en.wikipedia.org/wiki/Behavior-driven_development
  • http://ja.wikipedia.org/wiki/%E3%83%93%E3%83%98%E3%82%A4%E3%83%93%E3%82%A2%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA

2013年10月15日火曜日

Pair Programming: How Implement It?

Өмнө нь TDDPP-тэй хослуулан хэрэглэвэл маш үр дүнтэй гэж дурдсан билээ. Энэ удаа хэрхэн хослуулан хэрэглэх талаар жишээгээр тайлбарлая гэж бодов. 

Нэлээд хэдэн жилийн өмнө нэг сайхан жишээ олж харсан юм. Түүнээс давж бичиж чадахгүй болов уу гэж эмээж байгаа тул тэр жишээг монгол хэлнээ хөрвүүллээ.

Эндээс TDD болон PP-н талаар хангалттай ойлголт авах болов уу гэж найдаж байна. Энд дурдагдах зарим нэр томъёог шигтгээ оруулж (* Орч) тайлбарлахыг хичээлээ. Эргээд харсан чинь нэлээд урт бичлэг болжээ. Эцсийг нь хүртэл уншаад ойлгоогүй, дутуу дулимаг зүйл байвал сэтгэгдэл үлдээнэ үү!



Галт тэргэн доторх TDD

0. Эхлэл

Сайн байна уу? Амьдралдаа тохиолдсон хамгийн содон туршлагыг та бүхэнтэй хуваалцья. Энэ бол PP болон TDD-г хөдлөж буй галт тэргэн дотор
хэрэгжүүлсэн тухай яриа билээ.

PP бол хосоороо нэг программыг хөгжүүлэх арга бөгөөд алдаа багатай, уншигдац сайн, хялбар код бичих боломжоор хангадаг арга юм.

Харин TDD бол Test First Programming гэж [бас] хэлэгддэг эхлэж тест код бичээд, дараад нь жинхэнэ кодыг бичдэг арга юм.

Энэ 2 арга нь хоорондоо сайн таардаг, хоёул ихэд дэлгэрч буй XP-н туршлага билээ.

Жинхэнэ сэдэв рүү орохын өмнө TDD-н талаар бага зэрэг тайлбарлая. Өнөөг хүртэл "зохиомж" => "код" => "тест" гэсэн дараалалтайгаар программыг хөгжүүлэх арга зонхилж байсан билээ. Харин TDD-д "тест" => "код" => "зохиомж" гэсэн эсрэг дарааллаар программыг хөгжүүлдэг. Энд дурдсан "зохиомж" бол "кодлох" үйл явцын үр дүнд бий болсон дахин зохиомжлох ажиллагаа буюу refactoring юм.



Дээрх процесс ("тест" => "код" => "зохиомж")-т нэгжийн тест хийхэд зориулсан JUnit гэх мэт tool-г  хөгжүүлэлтийн чиг баримжаа болгодог.

JUnit-нь тестийн үр дүнг амжилтгүй бол улаан, амжилттай бол ногоон өнгөөр харуулах ба хөгжүүлэлт гурвалсан буюу "тест" => "код" => "зохиомж" хэлбэрээр өрнөнө.

TDD-г үндсэндээ дараах дараалалтайгаар хэрэгжүүлнэ.

Алхам Зорилго JUnit дэх өнгө
1
Тест бичих Улаан
2
Кодлох Ногоон
3
Refactoring Ногоон

Энэхүү документаар Китано (北野), Хиранабэ(平鍋) 2-н, Stack*-г сэдэв болгон галт тэрэг дотор явуулсан TDD (бодит явдал*)-н талаар өгүүлэх болно. 
* Энд дурдсан Stack нь C++-н standard library-д багтах stack<int>-н спек (spec)-г үндэс болгов. pop() утга буцаахгүй гэдгийг анхаарна уу!            
*2003.04 сард Хамамацүд зохиогдсон XP-н семинараас буцах замд болсон явдал. 
Дашрамд энэ үйл явдал Майбара(米原)-Хүкүи(福井) өртөөний хооронд галт тэрэг дотор өрнөсөн билээ.
            
             米原 ==== 長浜 ======== 敦賀 ======= 鯖江 ===== 福井



1.Майбара өртөө 
~Бүх үйл явдал эндээс эхлэсэн юм.~ 

Хиранабэ: Китано! Notebook чинь цэнэгтэй юу?
Китано: Яасан? Mэил шалгах гэсэн юм уу?
Хиранабэ: Үгүй ээ, үгүй. Өнөөдрийн семинар дээр хийсэн Stack-г бодитоор хийж үзмээр санагдаад.
Китано: Нээрээ, тийм шүү. Түр хүлээгээрэй. Асаагаадахъя.

(Цүнхнээсээ notebook гаргаж ирээд асаав)
Китано: За, Eclipse нээчихлээ.
Хиранабэ: За, TDD-р Stack хийхээс өмнө стекийн спек ямар байдгийг жагсаая.

  • isEmpty(): Стек хоосон бол true, үгүй бол false буцаана.                                         boolean isEmpty() 
  • size():  Стекийн хэмжээг буцаана.                                                                           int size() 
  • push(value): value-г  Стекийн оройд хийнэ.                                                             void push(int value) 
  • pop(): Стекийн оройн утгыг авч хаяна. Стек хоосон                                           бол java.util.EmptyStackException-г чулуудна.                                               void pop()                                                                     
  • top(): Стекийн оройн утгыг буцаана. Стек                                                         хоосон бол java.util.EmptyStackException-г чулуудна.                                   int top()
Китано: Классын нэр Stack байх нь ээ?
Хиранабэ: Тийм. Эхлээд би driver* болъё.
PP-д код бичиж буй талыг driver, хажуунаас дэмжигч талыг navigator гэнэ.  
Китано: Ойлголоо. Тэгвэл, эхлээд прожект үүсгэе.
Хиранабэ: Прожектын нэрийг Stack гэчих үү?
Китано: Тэгье. Прожект үүсгэчихээрээ, JUnit нэмчээрэй*. 

* Орч: Java прожектын classpath-д JUnit-н library (jar file)-г нэмэх
   classpath: Java class-г хаанаас олж болохыг заасан зам
   library: Логик холбоо бүхий хэсэг бүлэг class-н багц
   jar file: Java-н library file-г jar өргөтгөлтэй file-д хадгалдаг.
  
Хиранабэ: Прожект үүсгээд, JUnit нэмчихлээ. Test case үүсгэх үү?
Китано: Аанхаан. Stack-н тест юм чинь классыг StackTest гэж нэрлэе.
Хиранабэ: За. StackTest-н template-г үүсгэчихлээ.


   import junit.framework.TestCase;

  public class StackTest extends TestCase { 
  } 

2. Нагахама (長浜)Өртөө
~Assert First~

Хиранабэ: Эхлээд ямар тест бичих үү?
Китано: Харин ээ. Stack-г дөнгөж үүсгэсэний дараа хоосон, ө.х isEmpty()-н буцаах утга true байгаа эсэхийг шалгадаг ч юм уу? 
Хиранабэ: OK. Харгалзах тест кодыг бичье. testCreate() гэчихвэл болж байна уу?
Китано: Аанхаан болж байна.
    import junit.framework.TestCase; 
  
   public class StackTest extends TestCase { 
       public void testCreate() { 
           assertTrue(stack.isEmpty()); 
       } 
   } 

Хиранабэ: Энэ болж байна уу? 
Китано: Болж байна. Гэхдээ assertTrue-c өөр юм бичээгүй болохоор ...
Хиранабэ: Мэдээж, алдаа гарна. TDD-г хэрэгжүүлэх явцад, түрүүлээд assert метод бичдэг хэвшилтэй  юм л даа. Энэ нь юуг шалгахаа эхлээд тодорхой болгочоод, дараагаар энэ шалгалтыг хийх явцад шаардлагатай кодыг бичих учиртай.
Китано:  Аан, ойлголоо. Тийм учиртай байсан юм уу?
Хиранабэ: За, шаардлагатай кодыг бичье. Энэ болж байна уу? 

import junit.framework.TestCase; 

pulibc class StackTest extends TestCase { 
  public void testCreate() { 
   Stack stack = new Stack(); 
   assertTrue(stack.isEmpty()); 
  } 


Хиранабэ: Complile хийж үзэх үү? Eclipse дээр яаж хийдэг гэл ээ?
Китано: Хадгалангуут Compliler ажиллаж эхлэнэ. Би emacs-тай key binding хийсэн болохоор Ctrl-x s-р хадгална.
Хиранабэ: За хадгаллаа. Compile error гарчлаа.
Китано: Тийм байна. Stack класс байхгүй байна гэнэ. Үүсгэчие.
Хиранабэ: Eclipse дээр амархан үүсгэдэг гэж сонссон юм байна.
Китано: За байз. Compile error гарсан газар x гэснийг click 
хийвэл Stack классыг үүсгэх гэдэг менью гарч ирнэ*. 
* Eclipse-н энэ функцыг Quick Fix гэдэг.
Хиранабэ: Хо. Үнэхээр ашиглахад хялбар юм гээ ч.
Китано: Stack классын template бэлэн болчлоо.

 public class Stack {  
 

Хиранабэ: Дахиад compile error гарчлаа. За байз, compiler isEmpty методыг үүсгэ гээд байна. Ойлголоо, ойлголоо (дурамжхан). Ёсоор болгоё. Энд compile error арилгахад л хангалттай хамгийн бага код бичих ёстой болохоор boolean төрлийн default утга false-г буцаачъя*.

* Яг үнэндээ бол эхний тестийг давуулахгүйн тулд зориуд false буцааж байгаа юм.

Китано: Compiler ч гэсэн программчлагч болж оролцож байгаа юм шиг санагдчихлаа. PP(2уулаа) биш triple (3уулаа) болчлоо (инээв)!! Тоглоом хийх ч яахав.  TDD гэдэг чинь compiler-н тусламжтай хөгжүүлдэг арга юм аа даа.
Хиранабэ: Тийм шүү. Compiler-н хэлж буйг сайн сонсох юм бол дараачийн хийх ёстой алхамыг зааж өгдөг юм. Бас Eclipse мэтийн IDE байвал хүрэлцэхгүй байгаа метод, классыг үүсгэ гэж шавдуулдаг болохоор найдаж болох эд шүү (орч: eclipse-г хэлж байна).  
public class Stack { 
 public boolean isEmpty(){ 
   return false; 
 } 
}

3. Нагахама(長浜)-Цүрүга(敦賀) өртөөний хооронд 
~Эхлээд улаан, дараа нь ногоон, тэгээд refactoring!!~

Хиранабэ: Ингээд compile error алга болчлоо. Одоо тестлэе.
Китано: Улаан* болчлоо.
* JUnit ашиглан тест явуулж байгаа тохиолдолд энэ нь тест амжилтгүй болсоныг илтгэнэ.
Хиранабэ: Яг төлөвлөсний дагуу улаан боллоо. Ингээд нэгдүгээр алхам дууслаа. Иймэрхүү маягаар TDD-д эхлээд улаан болгоод дараа нь ногоон* болгох замаар нэг нэгээр нь шалгаад явдаг юм.  
* JUnit ашиглан тест явуулж байгаа тохиолдолд энэ нь тест амжиллтай болсоныг илтгэнэ.
Китано: Аан. Ингэж улаанаас ногоон болгож байгаа нь тест cпек (test specification document) дээр тест давсан эсэхийг check-лээд явахдаа төстэй юм байна.
Хиранабэ: Үргэлж ногоон байгаад байвал үнэхээр тест зөв хийгдэж байгааг мэдэхгүй, аягүй бол андуураад өөр тест хийж байгаа юм биш байгаа гэсэн түгшүүр төрнө.
Китано: Тэгвэл TDD-г улаан ногоон сөөлжихөөр хийх ёстой юм биз дээ. Temp алдахгүй зүгээр юм байна.
Хиранабэ: Яг зөв. Гол үндэс нь "улаан", "ногоон", "refactoring" гэсэн 3 тулгуур ойлголт. "Refactoring" яаж хийх талаар хожим үзүүлэмз.
Хиранабэ: Үндсэн сэдэв рүүгээ эргэж ороод, тест давах хамгийн бага кодыг бичих үү дээ?. 
Китано: Ммм... stack хоосон болохоор ... size гэх мэтийн хувьсагч ашиглаад ...
Хиранабэ: Юу? Тэрээ хүртлээ бодоод байгаа юм уу? Зүгээр л true буцаачихвал болоо биш үү?
Китано: A, тэгж болох юм уу?

public class Stack { 
 public boolean isEmpty(){ 
   return true; 
 }
}

Хиранабэ: Тестийг давах нь л одоогийн зорилго болохоор. Ирээдүйн талаар бодох хэрэггүй.
Хиранабэ: Ингэж энгийнээр кодлох ажиллагааг TDD-д оромдох-аргалах (fake it) гэдэг. 
Китано: Аан.
Хиранабэ: Тестлээд үзье.
Китано: Ногоон болчлоо. Ингээд 2-р алхам болчив уу? Гуравдугаар алхам refactoring хийх үү?
Хиранабэ: Үгүй. Одоохондоо давхардсан код байхгүй болохоор refactoring хэрэггүй. Бусад спекийг кодлочоод, шаардлага гарвал isEmpty()-г refactoring хийе.
Китано: За, ойлголоо. Тэгвэл push()-н тестийг хийж үзье тэгэх үү?
Хиранабэ: OK. push()-н тестийг top()-г ашиглаад хийж үзэх үү? Дахиад 1-р алхамаас эхлээд улаан төлвийг үүсгэе.
Китано: Методын нэрийг testPushAndTop() гэе.
Хиранабэ: Ойлголоо. assertEquals-г эхлээд бичье ...

import junit.framework.TestCase; 

pulibc class StackTest extends TestCase {
 public void testCreate() { 
   Stack stack = new Stack(); 
   assertTrue(stack.isEmpty()); 
 }
 public void testPushAndTop() { 
   Stack stack = new Stack(); 
   assertEquals(1, stack.top()); 
 }



Китано: Stack-н instance (хувь: санах ойд үүссэн объект) үүсгэж буй хэсгийн код давхардаж байна.
Хиранабэ: setup() метод руу оруулчих уу?. setup() бүх testXXX()-н өмнө дуудагддаг юм. ОК биз?
Китано: Тэгвэл testPushAndTop-г тайлбар болгоод, testCreate-г setUp методыг ашиглаад тестлээдэх үү?

 Stack stack; 
 public void setUp() { 
   stack = new Stack(); 
 } 
 public void testCreate() { 
   assertTrue(stack.isEmpty()); 
 } 
/* 
 public void testPushAndTop() { 
   Stack stack = new Stack(); 
   assertEquals(1, stack.top()); 
 } 

*/

Хиранабэ: Болж байна уу?
Китано: Болж байна. Тестлэе.
Хиранабэ: Ногоон хэвэндээ байна. testPushAndTop-г ч гэсэн тэгчих үү?
Китано: Тайлбараас гаргаад, а, push-г мартсан байна.
Хиранабэ: Өө, тийм байна. Хүлээж буй утга (expected value) маань 1 болохоор 1-г push хийх нь.

public void testPushAndTop() { 
  Stack stack = new Stack(); 
  stack.push(1) 
  assertEquals(1, stack.top()); 

}

Китано: Ингээд тест код бэлэн болчихлоо. Энэ чигээрээ бол compile error гарах болохоор, юуны өмнө compile error гарахааргүй болгочъё.
Хиранабэ: Эхлээд, push метод. Буцаах утга нь void болохоор юу ч бичилгүй орхичихъё. Дараачийнх нь top метод. return 0 гээд демо* хийчье.

* Oрч: программын бүрэн төгс болоогүй анхны хувилбар

 public void push(int value) { 
 } 

 public int top() { 
   return 0; 
 }

Китано: Ингээд compile error гарахааргүй боллоо. Энэ хэвээр нь ажиллуулбал улаарна. Тестлэж үзэх үү?  
Хиранабэ: Тестээ ажилууллаа. Улаан боллоо. Хүссэн ёсоор тест амжилгүй болов. Нэгдүгээр алхам дууслаа.
Китано: Тэгвэл, тест амжилттай болтол нь засъя. Одоо top методын буцаах утгыг 1 болгоно гэсэн үг үү?
Хиранабэ: Яг зөв. Fake хийнэ.

  public int top() { 
    return 1; 
  } 

Хиранабэ: Тестээ ажиллуулъя. Ногоорлоо. Төлөвлөсөн ёсоор тест амжиллтай болов. Хоёрдугаар алхам дууслаа. 
Китано: За, refactoring хийх үү?
Хиранабэ: Тэгье. push методоор оруулсан утгыг top методоор буцаахаар өөрчилье.

  private int value;

  public void push(int value) { 
    this.value = value; 
  } 
  public int top() { 
    return value; 
  }

Хиранабэ: Ингэчвэл яаж байна?
Китано: Болж байна. Тестлэж үзье. Ногоорох ёстой доо.
Хиранабэ: Тестээ ажилууллаа. Ногоон боллоо. Refactoring амжилттай. Гуравдугаар алхам болчлоо. Одоохондоо үүнээс илүү refactoring хийх боломжгүй тул дараачийн ажилдаа оръё.
Китано: Одоо size метод-г хийвэл яаж байна? 
Хиранабэ: OK. Push хийвэл size өсөх нь үү гэдэг тест бичье.
Китано: testPushAndSize() гэж нэрлэвэл зүгээр.
Хиранабэ: За. assertEquals-г эхлэж бичээд ... Энэ болж байна уу?

 public void testPushAndSize() { 
   s.push(1); 
   assertEquals(1, s.size()); 
 }

Китано: OK. Compile error гарахааргүй болгочих уу? size методыг return 0 болгочихвол болох нь ээ?
Хиранабэ: Тийм байна.

 public int size() { 
   return 0; 
 } 

Китано: Тестлээд үзье.
Хиранабэ: Run Test! Улаанаар эргэчихлээ. size()тест давсангүй гэсэн үг байх нь ээ.
Китано: Тэгвэл тест давж чадахаар болгоё.
Хиранабэ: size()-н буцаах утгыг нэг болгоё. Тэгээд тест... Ногоорчлоо.

 public int size() { 
   return 1; 
 } 

Хиранабэ: TDD-н 3 алхамыг эргээд нэг саная. Энэ 3 алхамыг үргэлж санаж явах юм шүү!


АлхамЗорилгоJUnit дэх өнгө
1
Тест бичихУлаан
2
КодлохНогоон
3
RefactoringНогоон



4.Цүрүга өртөө (敦賀駅) 
~ Triangulation-Гурвалжлах??~ 

Хиранабэ: Удалгүй Цүрүга өртөөнд хүрэх нь ээ.

Хиранабэ: Одоо жаахан аргаа өөрчлөөд дахиад нэг тест нэмье testPushAndSize()-д .
Китано: Яана гэсэн үг үү?
Хиранабэ: Ингэнэ гэсэн үг.

public void testPushAndSize() {
  s.push(1);
  assertEquals(1, s.size());
  s.push(2);
  assertEquals(2, s.size());
}

Хиранабэ: 2 удаа push хийхээр size нь 2 болно байх аа? Үүнийг шалгах тестийг нэмээд энэ тест, өмнөх тестийн аль алиныг нь давах хамгийн хялбар кодыг бодох учиртай юм. Дахиад улаан төлөвт оруулж байгаад, үргэлжлүүлдэг (код нэмэх, засах) нь TDD-н алтан дүрэм билээ. 

Китано: Тийм үү? Тэгвэл илүү зөв кодыг гаргаж авч чадах нь ээ. Ер анзаардаггүй явсанаас биш, би үргэлж иймэрхий маягаар кодлодог юм шиг санагдаад явчихлаа.
Хиранабэ: За, эхлээд энэ тестийг улаанаар эргэх нь үү шалгая.
Китано: За. Улайчлаа.
Хиранабэ: За байз, яаж энэ 2 тестийг 2ууланг нь зэрэг давах вэ? Китано чи байсан бол яаж хийх үү?
Китано: Size-н утгыг хадгалах instance хувьсагч Stack class-д нэмээд push()-г дуудах болгонд нэгээр нэмэгдүүлнэ. size() тэр утгыг буцаахаар засвал болно гэж бодож байна.
Хиранабэ: Санал нэг байна. Чи driver болох уу?
Китано: За.

public class Stack { 
  private int size; 

  public void push(int value) { 
     this.value = value; 
     ++size; 
  }

  public int size() {
    return size; 
  }
}

Китано: Энэ ямар уу?
Хиранабэ: За, compile хийгээд, тестлэе.
Китано: Run test! Ногоон болчлоо.
Хиранабэ: Амжиллттай боллоо. Энэ аргыг гурвалжлах (Triangulation) гэдэг. Хэд хэдэн test case бэлдээд, бүгдийг нь давах код бичсэнээр ерөнхий тохиолдлын шийдийг мөрдөж гаргах арга юм. Нэг тест (тухайн тохиолдол) бол fake хийгээд болчино. Үүнийг шинэ тест нэмж fake хийх (залилах) боломжгүй болгох замаар зөв хариуг мөрдөж гаргадаг. Гурвалжин хэмжилтийн арга шиг [нэг шулуун дээр орших] 2 цэгээс шулуун татаж тэдгээрийн огтлолцлоор ерөнхий шийд олдог TDD-н нэг pattern (загвар) юм.


Хиранабэ: За, энэ аргыг хэрэглээд одоо болтол fake хийсэн чигээрээ байгаа хэсэгт тест нэмээд явах уу? Анхаарал татсан газар байна уу?
Китано: Байна. isEmpty()-н [үргэлж] return true хийж байгаа нь анхаарал татаад байна. Яаж ийж байгаад засчмаар байна. Тийм учраас testPushAndTop() дотор push хийсний дараа isEmpty() false буцаана гэдгийг шалгамаар байна.

public void testPushAndTop() { 
  stack.push(1); 
  assertFalse(stack.isEmpty()); 
  assertEquals(1, stack.top()); 

Китано: Ийм болно.
Хиранабэ: Хо, сайн байна. Энэ ч гэсэн triangulation байна шүү дээ.
Китано: Тестээ ажиллуулаад улаан болж байгаа эсэхийг шалгаад яаж засахаа бодъё.
Хиранабэ: Яаж хялбар болгох вэ?

public boolean isEmpty() {
 return size == 0;
}

Китано: Ийм байх болов уу. size 0 утгатай бол true буцаах. 
Хиранабэ: ОК. Тестлээд үзье.
Китано:  Run Test! Ногоорчлоо.



5. Хокүрикү (北陸)-гийн хонгил ! Иччораи*! 
~Exception тест~ 


Китано: Дараачийн шинэ тестийг нэмэх үү?
Хиранабэ: Юуг тестлэх үү?
Китано: Pop-н тестийг хиймээр байна. Юуны өмнө огт push хийгээгүй үеийн pop-н тестийг нэмье.
Хиранабэ: Аа, тэр тохиололд EmptyStackException чулуудагдах нь уу гэдгийг шалгахгүй бол болохгүй нь ээ. Энээ хүртэлхээс жаахан өөр маягийн тест болох нь байна.
Китано: Тийм байна. Сонирхолтой тест болохоор хийж үзмээр байсан юм. Иймэрхүү байвал ямар уу? 

public void testEmptyPop() { 

  try { 
    stack.pop(); 
    fail(); 
  } catch (EmptyStackException exception) { 
  } 

* Орч: Иччорай - Хамгийн сайн зүйл  гэсэн утгатай үг. Иччора (一張羅) гэдэг үгний Фүкүи муж хавийн нутгийн аялга ажээ. Сонирхуулахад Фүкүи хавийн шинэ үеийн ардын дуу "Дэлгэр цаг" (イッチョライ節) дууны эхний мөрөнд Фүкүи мужын бахархал болсон газруудын нэг болох Хокурикүгийн хонгил гэж гардаг байна. Уг дууг 1937 онд энэ хонгилын нээлтэнд зориулж зохиосон гэдэг.

Хиранабэ: Аанхаан. pop()-н дараа fail() дуудагдвал (exception үүсэхгүй бол) тест амжилтгүй болно. ОК. Тэгээд pop()-дээр
Exception чулуудагдах болохоор [чулуудагдна гэж найдаж буй Exception-гcatch хийж (барьж авч) буй. Ммм.
Китано: Яасан?
Хиранабэ: Catch хийж буй Exception-ий хувьсагчийн нэр exception биш expected (найдаж буй) байвал илуу ойлгомжтой юм биш үү? 
Китано: Аа, тийм байна. Ойлголоо.
Китано: За, compile хийгээдэхье. Compile error гарчлаа. pop() байхгүй байна гэж байгаа болохоор pop()-г нэмлээ.
Хиранабэ: За, одоохондоо зөвхөн compile error-г арилгая.

public void pop() { 
}

Китано: Ийм болох нь ээ. Тестлээд үзье. Улаан болох ёстой доо.
Хиранабэ: За, улаалаа. pop()-н дараа fail() дуудагдаад байгаа болохоор тэр.
Китано: Тест давах хамгийн бага код бичих гээд үзье.
Китаноpop() дотроос Exception чулуудвал болох нь ээ?

public void pop() { 
 throw new EmptyStackException(); 
}

Хиранабэ: За, болж байна. Ногоон болох нь уу шалгая.
Китано: Ногоорчлоо (алгаа ташив).
Хиранабэ: Энэ бол Exception чулуудаж буй хэсгийг тестлэх арга. 
Китано: Энэ хэсгийг бас гурвалжлах аргаар шалгая.
Хиранабэ: Нэг удаа push-лэсний хийсний дараа pop-лоход Exception чулуудагдахгүй гэдэг тестийг хийж үзье. Та яаж хийхийг мэдэх үү?
Хиранабэ: Мэднэ. Ийм [тест] болно.

public void testPushAndPop() { 
 stack.push(1); 
 stack.pop(); 
 assertEquals(0, stack.size()); 

Хиранабэ: Шалгая. Улайх ёстой.
Китано: Аанхаан. Pop үргэлж Exception чулуудаж байгаа болохоор, Exception үүсч амжилтгүй болно. Run Test! Улаан боллоо.
Хиранабэ: Тэгвэл pop-г ингээд өөрчилчье.

public void pop() { 
 if (isEmpty()) 
   throw new EmptyStackException(); 
}

Хиранабэ: isEmpty() true, ө.х size 0 бол EmptyStackException-г чулуудна. 
Китано: За. OK, болж байна. size == 0 гэх юм болов уу гэсэн чинь, isEmpty()-ээр болох юм байна (инээмсэглэв).
Хиранабэ: Тестлээд үзье. Ногоон болчлоо (алга ташив).
Китано: Байз? top ч гэсэн EmptyStackException-г чулуудаж байгаа юм байна.
Хиранабэ: Тийм бил үү? Нээрээ тийм. За top-н exception-г тестлэе.

public void testEmptyTop() { 
 try { 
   stack.top();
   fail(); 
 } catch (EmptyStackException exception) { 
 } 
}

Хиранабэ: Энэ зөв үү?
Китано: Аанхаан. Тестлэе.  top()-д exception чулуудах код байхгүй болохоор fail() дуудагдаад, тест амжилтгүй болох ёстой.
Хиранабэ: ОК. Run Test! Улаан аслаа.
Китано: Тэгвэл pop()-той адилханаар засчихъя.   
Хиранабэ: Тэгье.

public int top() {
 if (isEmpty())
   throw new EmptyStackException();
 return value;
}

* top(), pop() 2 дээр хийж буй шалгалтыг ерөнхийлөх (тусад нь гаргах) хэрэгтэй гэдгийг анзаарсан ч бай яаран засах хэрэггүй. Юуны өмнө ногоон болгоод, дараад нь refactoring хийх нь зөв хандлага (TDD) юм.

Хиранабэ: Нэг ажиллуулаад шалгачия. Ногоон аслаа.
Китано: Гэхдээ top(), pop() 2-н EmptyStackException чулуудаж буй хэсэг адилхан логиктой болчлоо. Refactoring хийе.
Хиранабэ: Энэ удаагийн refactoring-д ExtractMethod* ашиглая.

public int top() {
  emptyCheck(); 
  return value; 

public void pop() { 
  emptyCheck(); 

private void emptyCheck() {
  if (isEmpty())
    throw new EmptyStackException();
}

* Орч: Ерөнхий кодыг тусад нь метод болгож гаргахыг хэлж буй бололтой.

Хиранабэ: Үүгээр ногоон болчвол refactoring амжиллтай болжээ гэсэн үг.


6. Хонгилийн үзүүрт?!

~TDD Оргил хэсэг (Last Spurt) ~

Хиранабэ: Ингээд TDD зөв голидролдоо орж эхэллээ шүү! Аль хэдий нь Хокүрикүгийн хонгил ард хоцорчээ. Одоо яах ёстой билээ.
Китано: Pop-лохоор оройн элементийг устгах.
Хиранабэ: Тийм. Хийгээгүй үлдсэн байна. Тест бичээдэхье.
Хиранабэ: Push-лээд pop-лоход size нь 0 болно, энэ тест.

public void testPushAndPop() {
  s.push(1);
  s.pop();
  assertEquals(0, s.size());
}

Китано: Тестээ ажиллуулъя.
Хиранабэ: Улаан болох ёстой доо. Ажиллуулаад үзье. Улаанаар эргэчихлээ. 
Китано: pop хийчээд байхад size-н утга өөрчлөгдөхгүй болохоор тэр дээ.
Хиранабэ: За, энэ тестийг амжилттай болгох код бичье.

public void pop() { 
 emptyCheck(); 
 size--; 

Китано: pop дуудагдахад size-н утгыг нэгээр хорогдуулна. Ингээд болоо шив дээ.
Хиранабэ: За, ажиллуулъя.
Китано: Ногоон боллоо.
Хиранабэ: Одоо яах билээ?
Китано: За байз, стекийн гол онцлог болох түрүүлж орсоныг сүүлд нь гаргадаг савыг хийгээгүй байгаа гэж бодож байна ... Одоогоор нэг л төвшинтэй стек байгаа болохоор ...
Хиранабэ: За, энэ тестийг бичээд үзье.

public void testPushPushPopTop() { 
  s.push(1); 
  s.push(2); 
  assertEquals(2, stack.size()); 
  s.pop(); 
  assertEquals(1, stack.top()); 

Китано: 2 удаа push, 1 удаа поп хийсний дараа top хийвэл эхний удаагаар push хийсэн утга  буцна гэсэн тест болох нь.
Хиранабэ: За, яг энэ байдалтайгаар ямар болох нь уу нэг шалгая. Run Test!
Китано: Улаан болчлоо. Түрүүн хэлсэнээр 1 төвшинтэй болохоор тэр үү?
Хиранабэ: Тэгвэл, олон төвшинтэй болгое. Яаж хийх үү?
Китано: Харин ээ, int төрлийн массив ашиглая.
Хиранабэ: You Have!*
Китано: I Have!*

* You Have! - Чамд даатгалаа шүү! 
   Онгоцны нисгэгч туслах нисгэгчид жолоо шилжүүлэхэд хэрэглэдэг хэллэг. 
* I have! - Ойлголоо! 
  "You Have!"-н хариу.

private int[] value = new int[10]; 

public void push(int value) { 
 this.value[size++] = value; 

public int top() { 
 emptyCheck(); 
 return value[size - 1];
}

Китано: size хувьсагчийг массивын индекс болгож ашиглана. Болж байна уу?
Хиранабэ: OK.
Китано: Run test!
Хиранабэ: Ногоон боллоо.
Китано: Ингээд бүх ToDo жагсаалт хоосон боллоо*. Аль хэдий нь Сабаэг өнгөрчиж, удалгүй Фүкүи. 50 мин хэртээ болов уу? энэ TDD-н дадлага.
Хиранабэ: Тийм. Гоё боллоо. Refactoring дахин хийх шаардлагагүй ч боллоо. Гоё цэвэрхэн код (clean code) ч гаргаж авч чадлаа.
Китано: Бүгдийг тэмдэглэж аваад бусдадаа дамжуулмаар юм байна.
Хиранабэ: Тийм шүү.

* Орч: Driver программ бичиж байх хооронд navigator дараагийн алхамуудыг төлөвлөх ба түүнийг driver-т хэлж ToDo жагсаалтанд нэмүүлдэг. Хийж буй зүйлээ дуусангуут дараачийн алхамын талаар зөвлөлдөөд, жагсаалтаас дараачийн ажлыг сонгоно. Гүйцэтгэж дууссан алхамыг жагсаалтаас хасах замаар кодлох нь PP-н бас нэгэн сонгодог арга юм.

//////////// Stack.java ///////////////
import java.util.EmptyStackException;

/**
 * @author koji,hiranabe
 */
public class Stack {
private int[] value = new int[10];
private int size;

public boolean isEmpty() {
return size == 0;
}

public int top() {
emptyCheck();
return value[size - 1];
}

public void push(int value) {
this.value[size++] = value;
}

public int size() {
return size;
}

public void pop() {
emptyCheck();
--size;
}

private void emptyCheck() {
if (isEmpty())
throw new EmptyStackException();
}
}

/////////////// StackTest.java /////////////
import java.util.EmptyStackException;
import junit.framework.TestCase;

/**
 * @author koji
 */
public class StackTest extends TestCase {

private Stack stack;

protected void setUp() {
stack = new Stack();
}

public void testCreate() {
assertTrue(stack.isEmpty());
}

public void testPushAndTop() {
stack.push(1);
assertFalse(stack.isEmpty());
assertEquals(1, stack.top());
stack.push(2);
assertEquals(2, stack.top());
}

public void testPushAndSize() {
stack.push(1);
assertEquals(1, stack.size());
stack.push(2);
assertEquals(2, stack.size());
}

public void testEmptyPop() {
try {
stack.pop();
fail();
} catch (EmptyStackException expected) {
}
}

public void testPushAndPop() {
stack.push(1);
stack.pop();
assertEquals(0, stack.size());
}

public void testPushPushPopTop() {
stack.push(1);
stack.push(2);
assertEquals(2, stack.size());
stack.pop();
assertEquals(1, stack.top());
}

public void testEmptyTop() {
try {
stack.top();
fail();
} catch (EmptyStackException expected) {
}
}
}


Дүгнэлт

Китано: Сонирхолтой байлаа.
Хиранабэ: Юу нь таалагдав?
Китано: Код нэмэх, өөрчлөх болгонд тест код нэмэгдэж байгаа нь.
Хиранабэ: Аанхаан. Тэгсэнээр бэлэн болсон код тэр чигээрээ тестлэгдсэн
төлөвт орж байгаа юм. 
Китано: Бичсэн кодондоо итгэлтэй болох юм билээ. Бас кодлох үед агуулга гэхээсээ илүү интерфейст анхааралаа хандуулах явдал.
Хиранабэ: Яг зөв. Ихэнхи тохиолдол зохиомжилчоод код бичдэг болохоор ихэнхи хүмүүс классын интерфейсийг хэрэгжүүлэгч (дотор=доторхи алгоритм) талаас хараад байдаг. Тест эхлэж бичсэнээр тэр классыг ашиглагчийн байр суурьнаас (гадна талаас нь) харж эхлэдэг зуршилтай болдог. Өөр?
Китано: ?
Хиранабэ:  Хамгийн чухал нь классын cпек тестлэх боломжтой спек болж хувирах явдал. Заримдаа тестлэх боломжгүй класс зохиох явдал тохиолддог. Харин XP-д энэ нь байж боломгүй үзэгдэл юм. Заавал тестлэгдэхгүй бол болохгүй. TDD-г хэрэгжүүлбэл тестлэгдэлт (testability-тестлэх боломж) -г үргэлж хангаж чадна.
Китано: Нээрээ тийм байна. Бас "улаан>ногоон>refactoring" нь хөгжүүлэлтийг ритмтэй болгоод, ямар ч байсан зугаатай юм.
Хиранабэ: Good Point. За, нэг дүгнээдэхье.

TDD-н өгөөж:
  • Бүх код тестлэгднэ.
  • Хэрэгжүүлэлт (алгоритм)-с илүү хэрэглэгч талын интерфейсийг чухалчилсан зохиомжтой болно.
  • Тестлэх боломж өндөртэй зохиомж бий болно.
  • Хөгжүүлэлт ритмтэй, зовлонгүй болно.
Китано: Хэрэгжүүлж үзээд жаахан залхуутай санагдлаа. Үргэлж fake хийгээд зүгшрүүлээд явах юм уу?
Хиранабэ: Тухайн нөхцөл байдлаас болно. Жишээ нь нэг мөрөөр бичиж болох кодыг fake хийх шаардлага байна уу? гэвэл хийсэн ч болно, хийхгүй байсан ч болно. Шууд эцсийн шийдийг бичих тохиолдол байна. Үүнийг тодорхой кодлолт (Obvious Implementation) гэнэ. Өөртэй итгэлтэй байгаа тохиолдолд тест код бичээд шууд эцсийн шийдийг бичих гэсэн зөвхөн улаан-ногооны хослолоор хийж болно. Харин өөртөө итгэлгүй байгаа бол оромдоод (fake), гурвалжлаад (triangulation), засалт (refactoring) хийгээд зүгшрүүлээд явах хэрэгтэй.
Энэ 2 аргыг солбиж хэрэглэх тухайд, хамтрагчдын өөртөө итгэх итгэл, чадвар, тухайн үеийн нөхцөл байдлаас хамаарна. Нойр дутуу ч юм уу толгой сэргэг биш үед бага багаар оромдож хийх нь ритмтэй болгох сайн тал бий.
Хиранабэ: Дахиад дээрх 3 аргыг нэгтгээд дүгнэе. Ж нь: int add(int a, int b) гэсэн метод бичмээр байлаа гэж саная. Зөв хариу нь return a + b; байг. Тэгвэл 3 арга байна. Эхлээд assertEquals(3, add(1, 2)); гэж тест код бичээд улаан болгоно. Эндээс 3 янзын аргаар цааш нь үргэлжлүүлж болно.
  1. Fake=>Refactoring                                                            Эхлээд return 3; гэж ногоон болгочоод, return a+b; 
  2. Fake=>Triangulation=>Refactoring                                   Эхлээд return 3; гэж кодлоод ногоон болгоно. Дараа нь assertTrue(4, add(3, 1)); гэж тестлээд, улаан болгоно. Хамгийн сүүлд нь return a + b;
  3. Тодорхой кодлолт ()                                                                         Шууд л return a + b;
Зургаар үзүүлбэл нэг иймэрхүү:

Хиранабэ: Бас нэг зүйл байна. Энд нэг өгөгдлийг танилцуулъя. TDD-г хэрэгжүүлсэн, хэрэгжүүлээгүй үеийн кодны чанарын ялгааны талаар ийм нэгэн өгөгдөл байна.
Хиранабэ: Энэ графикийг хар даа.
Хиранабэ: Энэ бол TDD болон ердийн хөгжүүлэлтийн харьцуулалт. Laurie Williams, Body George 2-н Хойд Каролинагийн их сургууль дээр явуулсан харьцуулсан туршилт. Боулингийн оноо боддог программыг 150 оюутнаар, хосоор программчлах аргаар хийлгэжээ. Программыг хөгжүүлэх хугацаа ердөө 75-хан минут байсан байна.
Китано: Энэ графикийг яаж унших ёстой вэ?
Хиранабэ: Уг туршилтаар 7 хүлээн авах тест* (acceptance test) бэлтгэсэн байсан бөгөөд эдгээр тестээс хэдийг нь амжилттай давсан эсэхийг хэвтээ тэнхлэгээр, түүнд харгалзах хувийг босоо тэнхлэгээр харуулжээ. 
Китано: Олон тестийг амжилттай давсан багийн дотроос TDD-р хөгжүүлэлт хийсэн багийн эзлэх хувь илт давуу байна шүү.
Хиранабэ: Тийм. Эндээс TDD-н ашигтай, сайн талыг ойлгосон байх гэж найдаж байна.
Хиранабэ: Ингэсгээд харихгүй бол ...
Китано: За тэгье. Баяртай.
Хиранабэ: Баяртай.

*Acceptance test: Захиалагч тал программыг хүлээн авах үед хийдэг тест

Жинхэнэ эхийг эндээс уншина уу! 
http://objectclub.jp/technicaldoc/testing/stack_tdd.pdf


Орчуулсан: Сарту Рэнцэн Батзоригт