Ачааллж байна...
Түвшин 3-т тавтай морил. Эхний хоёр Түвшинд бид кодыг хадгалах, хуваалцах, хамгаалах хэрэгслүүдийг сурсан. Git таны ажлын түүхийг хадгалж, буруу алхам хийвэл буцах боломж өгдөг. GitHub түүнийг дэлхийтэй хуваалцаж, бусдад харуулдаг. Гэхдээ эдгээрийн аль нь ч нэг чухал асуултад хариулдаггүй: таны код зөв ажиллаж байна уу? Git эвдэрсэн кодыг ч эвдрээгүй кодыг ч яг адилхан хичээнгүйлэн хадгална. Энэ Түвшинд бид тэр асуултад хариулах хэрэгслийг сурна: автомат тест.
Энэ хичээлд Бүлэг 1-ийн 2 дугаар хичээл, Бүлэг 3-ын 1 дүгээр хичээлийн нэгэн адил ганц ч command бичихгүй. Эхлээд бид өнөөг хүртэл кодоо хэрхэн шалгаж ирснийг дүгнэж, тэр арга яагаад удаан хугацаанд ажиллахаа больдгийг ойлгоно. Дараа нь тест гэж юу болох, юу өгдгийг зүйрлэлээр тодорхойлно. Эцэст нь хамгийн чухал хэсэг: тест юу биш вэ гэдгийг шударгаар ярина. Тест бол хүчирхэг хэрэгсэл ч ид шид биш, түүний хязгаарыг мэдсэнээр л та түүнийг зөв ашиглаж чадна.
Энэ сургалтын явцад бид кодоо олон удаа шалгасан. Өөрчлөлт бүрийн дараа python main.py бичиж, гаралтыг нүдээрээ хараад “20,500₮, зөв байна” гэж дүгнэсэн. 6.1 дүгээр хичээлд python -c "from report import average; print(average([]))" гэх мэт урт command-аар нэг функцийг тусад нь шалгасан. 6.3 дугаар хичээлд ангиллын нийт дүнг хэвлэж, "хоол 8500, унаа 12000" гэдгийг толгойдоо бодож баталгаажуулсан. Энэ бүхнийг гар аргаар шалгах буюу manual testing гэдэг. Энэ бол бүх програмистын эхэлдэг байгалийн, зөв арга.
Одоо бидний төсөлд хэдэн функц байгааг тоолъё. storage.py-д load_expenses, save_expenses. report.py-д total, count, average, total_by_category. formatting.py-д format_amount. Нийт долоон функц, тус бүр хэд хэдэн нөхцөлтэй: хоосон жагсаалт, ганц зарлага, олон зарлага, нэг ангилал, олон ангилал. Хэрвээ та өнөөдөр total функцийг өөрчилбөл юуг дахин шалгах ёстой вэ? Зөвхөн total-ийг үү? Үгүй. average total-ийг дууддаг. main.py-ийн гаралт total-ийг ашигладаг. Тэгвэл average-ийн хоосон жагсаалтын нөхцөлийг дахин шалгах уу? 6.1 дүгээр хичээлд зассан тэр алдаа буцаж гарч ирээгүй гэдэгт итгэлтэй байна уу?
Энд гар аргаар шалгах аргын үндсэн асуудал ил гарч байна. Код томрох тусам шалгах ёстой зүйлсийн тоо өсдөг, харин таны цаг, анхаарал, тэвчээр өсдөггүй. Хэдэн арван функцтэй төсөлд өөрчлөлт бүрийн дараа бүх нөхцөлийг гараар шалгах нь бодитоор боломжгүй. Тиймээс хүмүүс сүүлд өөрчилсөн хэсгээ л шалгаж, бусдыг нь "ажиллаж байгаа байх" гэж найддаг. Тэгээд нэг өдөр хоёр долоо хоногийн өмнө ажиллаж байсан зүйл чимээгүйхэн эвдэрсэн байдаг. Шинэ өөрчлөлтийн улмаас хуучин ажиллаж байсан зүйл эвдрэхийг regression гэдэг бөгөөд энэ бол програм хангамжийн хамгийн түгээмэл алдааны төрлүүдийн нэг. Бүлэг 1-ийн 1 дүгээр хичээлд дурдсан "нэг алдааг засаад хоёр долоо хоногийн дараа өөр нэг хэсэг чимээгүйхэн эвдэрсэн" туршлага бол яг regression юм.
Олон хаалгатай том барилгын эзэн гэж өөрийгөө төсөөлөөрэй. Шөнө бүр бүх хаалга түгжээтэй эсэхийг шалгах ёстой. Эхэндээ барилга таван хаалгатай байхад та өөрөө тойроод шалгадаг. Гэхдээ барилга өргөжиж, хаалга тавь болоход шөнө бүр бүгдийг шалгах нь цуцмаар, удаан ажил болно. Та ядрах тусам зарим хаалгыг алгасаж, "өчигдөр түгжээтэй байсан" гэж найддаг. Тэгээд нэг шөнө тэр алгассан хаалганы нэг онгорхой үлддэг.
Одоо харуул хөлсөлсөн гэж төсөөлөөрэй. Та түүнд жагсаалт өгнө: "энэ хаалга түгжээтэй байх ёстой, тэр цонх хаалттай байх ёстой, гэрэл унтраалттай байх ёстой." Харуул хэзээ ч ядардаггүй, хэзээ ч уйддаггүй, хэзээ ч алгасдаггүй. Та хүссэн үедээ "шалга" гэхэд тэр жагсаалтын бүх зүйлийг хэдхэн секундэд шалгаад, "бүгд зөв" эсвэл “3 дугаар хаалга онгорхой байна” гэж хэлнэ. Тавин хаалга байсан ч зуун хаалга байсан ч ялгаагүй.
Автомат тест бол яг тэр харуул. Тест бол таны кодыг тодорхой оролтоор ажиллуулж, үр дүн нь хүлээгдэж буй утгатай тэнцүү эсэхийг шалгадаг жижиг программ юм. Жишээ нь “total функцэд 5000 болон 3000 гэсэн хоёр зарлага өгөхөд 8000 буцаах ёстой” гэдгийг кодоор бичсэн өгүүлбэр. Та ийм өгүүлбэрүүдийг нэг удаа бичнэ. Тэгээд хэдэн мянган удаа ч дахин ажиллуулж болно, тус бүр хэдхэн миллисекунд. Хэрвээ хэзээ нэгэн цагт таны өөрчлөлт total-ийг эвдвэл харуул шууд "энэ тест уналаа" гэж хашгирна.
Харуулын зүйрлэлийн нэг нарийн хэсгийг одоо тэмдэглэе: харуул зөвхөн жагсаалтад байгаа хаалгуудыг шалгадаг. Хэрвээ та түүнд арын хаалганы тухай хэлээгүй бол тэр түүнийг хэзээ ч шалгахгүй. Энэ санаа энэ хичээлийн хамгийн чухал хэсэгт буцаж ирнэ.
Энэ хичээлд бид юуг ч ажиллуулахгүй ч тест ямар харагддагийг урьдчилан харах нь санааг тодорхой болгоно. Python-ий assert гэдэг түлхүүр үгийг та өмнөх сургалтаас санаж магадгүй: энэ нь нөхцөл үнэн эсэхийг шалгаж, худал бол алдаа өгдөг. Тест ихэвчлэн яг ийм харагддаг.
python
def test_total_of_two_expenses():
expenses = [{"amount": 5000}, {"amount": 3000}]
assert total(expenses) == 8000Энэ гурван мөр бүтэн өгүүлбэр шиг уншигддаг. Функцийн нэр юуг шалгаж байгааг хэлнэ: хоёр зарлагын нийлбэр. Хоёр дахь мөр оролтыг бэлтгэнэ. Гурав дахь мөр “total энэ оролтод 8000 буцаах ёстой” гэж мэдэгдэнэ. Хэрвээ буцаасан утга 8000 бол тест давна, өөр утга бол унана. Ийм жижиг функцуудыг цуглуулж, нэг command-аар бүгдийг нь ажиллуулдаг хэрэгслийг test runner гэдэг. Энэ сургалтад бид Python-ий хамгийн өргөн хэрэглэгддэг test runner болох pytest-ийг ашиглана. Python-д unittest гэдэг стандарт сангийн тестийн хэрэгсэл ч бий, гэхдээ pytest илүү энгийн бичлэгтэй, уншихад илүү ойлгомжтой гаралттай учраас өнөөдөр ихэнх шинэ төсөл түүнийг сонгодог.
Харуулын зүйрлэлээс илүү тестийн дөрвөн бодит ашгийг нэрлэе.
Нэгдүгээрт, regression-оос хамгаална. Энэ бол хамгийн шууд ашиг. Нэг удаа бичсэн тест хэзээ ч мартдаггүй. 6.1 дүгээр хичээлд average-ийн хоосон жагсаалтын алдааг зассан. Хэрвээ тэр үед “average хоосон жагсаалтад 0 буцаах ёстой” гэсэн тест бичсэн бол хэн нэгэн, эсвэл ирээдүйн та, тэр “if not expenses” мөрийг санамсаргүй устгах үед тест шууд унаж, алдаа буцаж ирснийг мэдэгдэх байсан.
Хоёрдугаарт, өөрчлөх зориг өгнө. Энэ санааг та аль хэдийн мэднэ. Бүлэг 1-д Git туршилт хийх айдсыг арилгадаг гэж ярьсан: эвдэрвэл буцаж болно. Тест үүнийг гүйцээдэг: Git танд буцах боломж өгдөг бол тест танд хэзээ буцах ёстойгоо мэдэх боломж өгдөг. Сайн тесттэй төсөлд та кодоо зоригтой өөрчилж, тестүүдээ ажиллуулаад, бүгд давбал юу ч эвдрээгүй гэдэгт итгэлтэй болно.
Гуравдугаарт, кодын зан төлөвийг баримтжуулна. Тест бол "энэ функц ийм оролтод ийм үр дүн өгдөг" гэсэн жишээнүүдийн цуглуулга. Хэдэн сарын дараа total_by_category хоосон жагсаалтад юу буцаадгийг мартсан бол тест тань хариулна. Энэ баримт бичиг README-ээс ч илүү найдвартай, учир нь буруу болсон мөчид тест унаж, өөрөө мэдэгддэг.
Дөрөвдүгээрт, кодын бүтцийн тухай санал өгнө. Зарим функц тестлэхэд маш хялбар, зарим нь маш хэцүү байдаг. Тестлэхэд хэцүү код ихэнхдээ хэт олон зүйл нэг дор хийдэг код байдаг. Энэ сэдвийг бид 9.2 дугаар хичээлд нэг практик жишээгээр үзнэ. Одоохондоо тест бичих оролдлого таны кодыг илүү энгийн, тодорхой болгох чиглэлд түлхдэг гэдгийг мэдэхэд хангалттай.
Одоо энэ хичээлийн хамгийн чухал хэсэг ирлээ. Тестийн давуу талыг сонсоод "бүх тест давбал миний код төгс" гэж бодох хүсэл төрдөг. Энэ бол аюултай буруу ойлголт тул тестийн хязгаарыг тодорхой хэлье.
Тест кодын зөвийг баталдаггүй. Тест зөвхөн таны бичсэн тохиолдлуудыг шалгадаг. Харуулын зүйрлэлийг санаарай: тэр зөвхөн жагсаалтад байгаа хаалгуудыг шалгадаг. 6.1 дүгээр хичээлийн алдааг дахин бодъё. Хэрвээ бид тэр алдааг олохоос өмнө average-д “5000, 3000 гэсэн хоёр зарлагын дундаж 4000” гэсэн тест бичсэн бол тэр тест амжилттай давах байсан. Тэгсэн ч хоосон жагсаалтын алдаа кодонд хэвээрээ байх байсан, учир нь хэн ч харуулд тэр хаалганы тухай хэлээгүй. Програмчлалын түүхэнд алдартай нэг санаа бий: тест алдаа байгааг харуулж чаддаг ч алдаа байхгүйг хэзээ ч баталж чаддаггүй.
Тест ч мөн код бөгөөд алдаатай байж болно. Хэрвээ та хүлээгдэж буй утгыг буруу бичвэл, жишээ нь 5000 болон 3000-ийн нийлбэрийг 9000 гэж бичвэл, зөв код тань тестийг уналгах болно. Харин илүү аюултай нь эсрэг тохиолдол: код болон тест хоёулаа ижил алдаатай байвал тест давж, алдаа нуугдана. Иймээс тестэд хүлээгдэж буй утгыг кодоосоо хуулж биш, гараар, бие даан бодож бичих нь чухал.
Тест таны бодолхийлэл болон программыг жинхэнэ байдлаар ашиглахыг орлохгүй. Тест бүр "ийм оролтод ийм гаралт" гэсэн жижиг баталгаа ч программ бүхэлдээ хэрэглэгчид тустай юу, гаралт ойлгомжтой юу, мессежүүд найрсаг уу гэдгийг зөвхөн та өөрөө ашиглаж үзэхэд л мэднэ. Автомат тест гар аргаар шалгахыг бүрмөсөн орлодоггүй, харин тэр уйтгартай, давтагдах хэсгийг нь хүний мөрнөөс авдаг.
Эдгээр хязгаарыг мэдсэнээр та тестийг зөв газар нь тавина: тест бол хүчирхэг сүлжээ, гэхдээ нүхтэй сүлжээ. Түүний нүхийг багасгах арга бол чухал тохиолдлуудыг, ялангуяа хил дээрх тохиолдлуудыг ухамсартайгаар сонгох явдал. Хил дээрх тохиолдол буюу edge case гэдэг нь хоосон жагсаалт, ганц элемент, тэг, сөрөг тоо, маш том утга зэрэг кодын "ирмэг" дээрх нөхцөлүүд юм. Алдаа ихэвчлэн яг тэнд нуугддаг, 6.1 дүгээр хичээлийн хоосон жагсаалт шиг.
Тестүүдийг ихэвчлэн хамрах хүрээгээр нь гурав ангилдаг. Unit test буюу нэгжийн тест нэг жижиг хэсгийг, ихэвчлэн нэг функцийг тусад нь шалгадаг. Дээрх total-ийн тест бол unit test. Integration test буюу нэгтгэлийн тест хэд хэдэн хэсэг хамтдаа зөв ажиллаж байгааг шалгадаг, жишээ нь файлаас ачаалж, тооцоолж, үр дүн гаргах бүхэл замыг. End-to-end test буюу эхнээс төгсгөл хүртэлх тест программыг хэрэглэгчийн ашигладагтай яг адил, бүхэлд нь ажиллуулдаг.
Эдгээрийг ихэвчлэн пирамид хэлбэрээр төсөөлдөг. Пирамидын суурь өргөн: олон тооны, хурдан, жижиг unit test. Дунд хэсэг нь цөөн integration test. Орой нь нарийн: хамгийн цөөн, хамгийн удаан end-to-end test. Энэ хэлбэрийн учир нь жижиг тест хурдан ажиллаж, унахдаа асуудлыг яг хаана байгааг тод заадаг бол том тест удаан, унахдаа шалтгааныг нь олоход хэцүү байдаг. Энэ сургалтад бид голчлон пирамидын суурь болох unit test бичнэ, 9.1 дүгээр хичээлд файлтай ажилладаг кодыг тестлэхдээ integration test-ийн хил рүү бага зэрэг ойртоно.
Энэ Түвшин гурван Бүлэгтэй. Бүлэг 7-д pytest суулгаж, анхны тестээ бичиж, assert-ийг гүнзгий сурч, амжилтгүй болсон тестийн гаралтыг мөр мөрөөр уншиж сурна. Сүүлийнх нь энэ сургалтын алдааны мессеж унших уламжлалын үргэлжлэл бөгөөд хамгийн чухал ур чадваруудын нэг. Бүлэг 8-д тестүүдээ фолдерт зохион байгуулж, олон тестэд нэг бэлтгэлийг хуваалцах fixture, нэг тестийг олон тохиолдлоор ажиллуулах parametrize, алдаа гарах ёстойг шалгах арга зэргийг сурна. Бүлэг 9-д бодит код, тэр дундаа файлтай ажилладаг кодыг тестлэж, эцэст нь зарлагын бүртгэлдээ жинхэнэ тестүүдийн цуглуулга бичнэ. Тэр ажлыг салаа дээр хийж, pull request-ээр нэгтгэх тул Түвшин 1, 2-ын дадлууд дахин хэрэглэгдэнэ.
Хүсвэл цаас, үзэг аваад зарлагын бүртгэлийнхээ функц бүрийг бичээрэй: total, count, average, total_by_category, format_amount. Тус бүрийн хажууд хоёр гурван жишээ бичнэ үү: ямар оролт өгөхөд ямар үр дүн гарах ёстой вэ. Жишээ нь “format_amount(20500) → ‘20,500₮’”. Ердийн тохиолдолтой зэрэгцээд хил дээрх тохиолдлуудыг бодоорой: хоосон жагсаалт, ганц зарлага, тэг дүн. Хүлээгдэж буй үр дүнг кодыг харалгүй, толгойдоо бодож бичээрэй. Энэ жагсаалт бол таны харуулын хаалгуудын жагсаалт бөгөөд дараагийн хичээлүүдэд тэдгээр нь жинхэнэ тест болно.
Тест бол зөвхөн том компаниудад хэрэгтэй. Бүлэг 1-ийн 1 дүгээр хичээлд энэ буруу ойлголтыг товч дурдсан. Одоо дэлгэрүүлье: тест жижиг төсөлд хамгийн хялбар бичигддэг бөгөөд ашиг нь шууд мэдрэгддэг. Гурван мөрийн тест ганц хүний хоёр долоо хоногийн өмнөх алдааг дахин гаргахаас хамгаалахад том багийнхтай яг адил үнэ цэнтэй. Мөн дадлыг жижиг төсөл дээр эзэмшсэн хүн том төсөлд ороход аль хэдийн бэлэн байдаг.
Тест бичих нь ажлыг хоёр дахин нэмэгдүүлнэ. Шударгаар хэлэхэд тест бичихэд цаг ордог, эхэндээ мэдэгдэхүйц. Гэхдээ энэ цаг хожим олон дахин буцаж ирдэг: гараар дахин дахин шалгах цаг, regression-ийг хайж олох цаг, "энэ өөрчлөлт юуг эвдэх вэ" гэж санаа зовох цаг бүгд багасна. Тестийн цаг бол даатгалын төлбөртэй адил: өдөр бүр бага зэрэг, гэхдээ асуудал гарах үед олон дахин нөхөгдөнө.
Тестийг дараа нь бичнэ. "Эхлээд программаа дуусгаад дараа нь тест нэмнэ" гэдэг бол хамгийн түгээмэл төлөвлөгөө бөгөөд ихэнхдээ хэзээ ч биелдэггүй. Программ дууссаны дараа тест бичих сэдэл буурч, код тестлэхэд хэцүү бүтэцтэй болсон байдаг. Хамгийн сайн цаг бол функцийг бичмэгц тест бичих, эсвэл бүр түүнээс өмнө. Энэ сургалтад бид одоо байгаа кодонд тест нэмэх тул "дараа нь" гэсэн нөхцөлийг туулах боловч цаашдын ажилдаа функц бүртэйгээ тестээ зэрэг бичих дадлыг эрхэмлээрэй.
Илүү олон тест үргэлж илүү сайн. Тестийн чанар тооноос илүү чухал, 5.4 дүгээр хичээлийн repository-ийн тухай санаатай адил. Нэг зүйлийг арван янзаар шалгасан арван тест хил дээрх тохиолдлуудыг хамарсан гурван сайн тестээс бага үнэ цэнтэй байж болно. Мөн хэт олон, хоорондоо давхацсан тест кодыг өөрчлөхөд хүндрэл үүсгэдэг: нэг өөрчлөлт хорин тест уналгавал засахад цаг их ордог. Зорилго нь чухал зан төлөвийг тодорхой шалгах, бүх зүйлийг хэдэн удаа шалгах биш.
Гар аргаар шалгах нь эхлэхэд зөв арга ч код томрох тусам шалгах зүйлс өсөж, хүн алгасаж эхэлдэг, улмаар regression буюу хуучин ажиллаж байсан зүйл чимээгүйхэн эвдрэх аюул үүсдэг. Автомат тест бол кодыг тодорхой оролтоор ажиллуулж, үр дүнг хүлээгдэж буй утгатай харьцуулдаг жижиг программ, хэзээ ч унтдаггүй харуул юм. Тест regression-оос хамгаалж, өөрчлөх зориг өгч, кодын зан төлөвийг баримтжуулж, кодын бүтцийн тухай санал өгдөг. Харин тест кодын зөвийг баталдаггүй: зөвхөн бичигдсэн тохиолдлуудыг шалгадаг, тест өөрөө алдаатай байж болно, программыг жинхэнэ байдлаар ашиглахыг орлодоггүй. Тиймээс хил дээрх тохиолдлуудыг ухамсартайгаар сонгох нь чухал. Тестүүд unit, integration, end-to-end гэсэн гурван түвшинтэй бөгөөд пирамидын өргөн суурь нь олон жижиг unit test байна. Энэ сургалтад Python-ий хамгийн өргөн хэрэглэгддэг pytest-ийг ашиглана.
Дараагийн хичээлд онолоос практик руу шилжинэ. pytest-ийг venv-д суулгаж, тестийн файл болон функцийг нэрлэх дүрмийг сурч, жижиг бодит функцэд анхны тестээ бичнэ. Дараа нь pytest-ийг ажиллуулж, амжилттай тестийн гаралтыг мөр мөрөөр нь уншина.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.