38-წუთიანი სამუშაო სესია ტესტებზე, რომლებიც თავს ირჩენენ: პირამიდა, კარგი ტესტის ანატომია, წითელი-მწვანე-რეფაქტორინგის ციკლი, ტესტ-დუბლიორები, ინსტრუმენტები, რომლებიც ამ ყველაფერს უშვებენ, და როგორ დაწეროთ ტესტები, რომლებიც ბაგებს იჭერენ და არა აცემენტებენ.
ტესტები იმას არ ემსახურება, რომ დღეს თქვენი სიმართლე დაამტკიცოთ. ისინი იმისთვისაა, რომ შემდეგმა ადამიანმა — ხშირად მომავალმა თქვენ — ამ კოდის შეცვლა სუნთქვაშეკრული არ მოუწიოს. ბაგის დაჭერა ყველაზე იაფი მაშინაა, როცა მას წერთ, შემდეგ კი ფასი მხოლოდ იზრდება.
ღირს ბაგის გასწორება, როცა მას აკრეფის დროსვე იჭერთ.
…როცა უკვე შემერჯულია და მასზე სხვამ ააშენა.
…როცა უკვე პროდაქშენშია და ღამის 2 საათზე გიძახებენ.
ბაგი, რომელიც ჩუმად გადის და მონაცემებისადმი ნდობას ღრღნის.
მიზანი 100% დაფარვა არაა. მიზანი დახარჯულ წუთზე მიღებული ნდობაა.
ყველა ტესტი ერთნაირად არ ჯდება. პირამიდა ბიუჯეტია: შემოწმებების უმეტესობა იაფ, სწრაფ შრეზე ჩამოწიეთ, ნელი და მყიფე შრე კი იმ რამდენიმე მარშრუტს დაუტოვეთ, რომელსაც ის მართლა სჭირდება.
გავრცელებული ორიენტირია დაახლოებით 70% იუნიტი, 20% ინტეგრაცია, 10% ბოლოდან ბოლომდე — ბიუჯეტის უმეტესობა სწრაფ ფუძეზე, რომელიც ნელი, სრულსისტემური წვერისკენ თხელდება.
რაც უფრო მაღლა, მით უფრო რეალისტურია, მაგრამ ნელი, არასტაბილური და გასამართად რთული. ნდობას ფასი აქვს; პირამიდა ანგარიშს გონივრულ ფარგლებში ატარებს.
გუნდების უმეტესობა თავდაყირა პირამიდისკენ მიდის: ნელი ბოლოდან ბოლომდე ტესტების სქელი შრე და თითქმის არცერთი იუნიტ-ტესტი. ნაკრები 40 წუთს ითხოვს, შემთხვევით ვარდება და წითელს აღარავინ ენდობა.
ლოგიკა ქვემოთ ჩამოწიეთ: ფასდადების წესი იუნიტად დატესტეთ და არა ჩექაუთში კლიკებით. E2E რამდენიმე კრიტიკულ მარშრუტს დაუტოვეთ — რეგისტრაცია, ჩექაუთი, ფულის გზა.
ჰგავს კორექტურას: ყოველ წინადადებას მართლწერა შეუმოწმეთ, მაგრამ მთელი ესეს ხმამაღლა წაკითხვა მხოლოდ ორიოდე ჯერ დაგჭირდებათ.
ტესტი, რომელსაც ხუთ წამში წაიკითხავთ, ისეთი ტესტია, რომელსაც ხალხი მწვანედ შეინარჩუნებს. ყოველთვის ერთნაირად დაასტრუქტურეთ, ქცევის მიხედვით დაასახელეთ და ერთი რამ შეამოწმეთ.
test("test1", () => { const c = new Cart() c.add(book); c.add(pen) expect(c.total()).toBe(30) c.applyCoupon("SAVE10") // second behavior… expect(c.total()).toBe(27) expect(c.items.length).toBe(2) // …and a third }) // what broke? the name won't tell you.
test("applies a 10% coupon to the cart total", () => { // Arrange const cart = cartWith(book30) // Act cart.applyCoupon("SAVE10") // Assert expect(cart.total()).toBe(27) })
ტესტის სახელი დოკუმენტაციაა. მკითხველმა უნდა იცოდეს, რა გატყდა, სხეულის გახსნის გარეშე. აღწერეთ სცენარი და მოსალოდნელი შედეგი — არასდროს test1.
დაარღვიეთ რომელიმე მათგანი და ნაკრები გახდება ნელი, არასტაბილური ან ჩუმად უსარგებლო. Isolated და Repeatable ისინია, რომლებსაც გუნდები ყველაზე ხშირად არღვევენ — ჩვეულებრივ საერთო მონაცემთა ბაზებითა და საათით.
TDD ჩვეულ თანმიმდევრობას თავდაყირა აყენებს: ჯერ დაწერეთ ჩავარდნილი ტესტი, შემდეგ ყველაზე მარტივი კოდით გაატარეთ და ბოლოს დაალაგეთ. ტესტი მოგვიანებით მოსაფიქრებელი არაა — ის სპეციფიკაციაა, რომლისკენაც კოდს წერთ.
პატარა ციკლები, თითო რამდენიმე წუთი. ტესტი წითლდება მანამ, სანამ კოდი გაჩნდება, შემდეგ კი მას მწვანემდე მიჰყავს.
დისციპლინა პაწაწინა ნაბიჯებს გაიძულებთ. ცნობილ, გამართულ მდგომარეობას ყოველთვის სულ რამდენიმე წუთით სცილდებით.
// written, then "how do I even test this?" function checkout() { const now = Date.now() // hidden clock const db = new Postgres() // hidden dependency // 60 lines of mixed I/O + rules… } // untestable without a real DB and the right date.
// the test you wished you could write FIRST: test("10% off orders over $100", () => { const price = discount(120) expect(price).toBe(108) }) // forces a pure fn: inputs in, result out. function discount(total) { /* no clock, no DB */ }
TDD-ის ნამდვილი საჩუქარი ტესტები არაა — არამედ ის, რომ ძნელად სატესტი კოდი ტყუილად როდი არის ძნელად სატესტი, და ტესტის ჯერ დაწერა ამ ტკივილს მაშინ ამოატივტივებს, როცა მისი გასწორება ჯერ კიდევ იაფია.
იმისთვის, რომ ერთეული იზოლაციაში დატესტოთ, მის ნამდვილ დამოკიდებულებებს — მონაცემთა ბაზას, გადახდის gateway-ს, საათს — მართვად შემცვლელებს უნაცვლებთ. "მოკს" ყველა მათგანზე ამბობენ, მაგრამ ხუთი ტიპი სხვადასხვა საქმეს აკეთებს.
დუბლიორი ნამდვილი gateway-ის ნაცვლად შეიყვანეთ. SUT განსხვავებას ვერ ხედავს — სწორედ ამას გაძლევთ დამოკიდებულებების ინექცია.
// passed only to satisfy a signature; never called const logger = {} as Logger new Invoice(items, logger) // this test never logs
// pre-programmed responses, no logic of its own const rates: RateApi = { usdTo: () => 0.8 // always returns 0.8, whatever the input } expect(convert(100, rates)).toBe(80)
const sent: string[] = [] const mailer: Mailer = { send: (to, body) => { sent.push(to) } // records } notifyAll(users, mailer) expect(sent).toEqual(["a@x.io", "b@x.io"])
// the expectation IS the assertion const gateway = mock<Payment>() expect(gateway.charge).toHaveBeenCalledWith(2000, "usd") // the test fails if charge() wasn't called exactly so
// working logic, just not production-grade class InMemoryUserRepo implements UserRepo { private rows = new Map() save(u) { this.rows.set(u.id, u) } findById(id) { return this.rows.get(id) } }
მწვანე 100%-იც კი ბაგებს გაუშვებს, არასტაბილური ნაკრები კი მის არარსებობაზე უარესია. იმის ცოდნა, რა არ დატესტოთ, ისევე მნიშვნელოვანია, როგორც იმისა, რა დატესტოთ.
test("runs without error", () => { calculateTax(order) // 100% line coverage… }) // no assertion → tax could be -∞ and this stays green.
test("charges 8% tax, rounded to the cent", () => { expect(calculateTax({ subtotal: 49.99 })) .toBe(4.00) }) // same line covered — but now it actually checks the rule.
არასტაბილური ტესტი კოდის ცვლილების გარეშე ხან გადის, ხან ვარდება. ყოველი ცრუ განგაში გუნდს წითელის იგნორირებას ასწავლის — ნაკრები კი, რომელსაც არავინ ენდობა, მკვდარი ტვირთია.
Date.now(), სასაათო სარტყლები, დაძინებები. შეიყვანეთ საათი.sleep(500) პირობის მოლოდინის ნაცვლად.ჰგავს კვამლის სენსორს, რომელიც შემთხვევით წრიპინებს — ხალხი ბატარეას ამოაძვრენს და მერე ის ვეღარაფერზე გაფრთხილებთ.
დატესტეთ დაკვირვებადი ქცევა სტაბილურ საზღვარზე: ამ შემავალი მონაცემებითა და ამ მდგომარეობით ერთეული ამ გამოსავალს ან ამ ხილულ ეფექტს იძლევა. თუ სისწორის შემნარჩუნებელი რეფაქტორინგი ტესტს აწითლებს, ეს ტესტი იმას ამოწმებდა, როგორ, და არა იმას, რა.
დაფარვა იმ ქვედა ზღვარზე მიმართეთ, რომელიც აშკარა ხარვეზებს იჭერს (ვთქვათ, 70–80% შეცვლილ კოდზე), დაზოგილი ენერგია კი უკეთეს მტკიცებებსა და სასაზღვრო შემთხვევებს — ზღვრებს, ცარიელ მნიშვნელობებს, შეცდომებს — დაუთმეთ და არა ბოლო, დაუტესტავი პროცენტის დევნას.
ინსტრუმენტები პირამიდის მიხედვით იყოფა. ტესტების გამშვები იუნიტ- და ინტეგრაციული შრეების სამუშაო ცხენია; ბოლოდან ბოლომდე (E2E) ფრეიმვორკი კი ნამდვილ ბრაუზერს მართავს თხელი წვერისთვის. ჩვეულებრივ თითო-თითოს ირჩევთ და მიდიხართ — ნუ იტანჯებით.
ერთი და იგივე გამშვები ჩვეულებრივ ფარავს იუნიტ- და ინტეგრაციულ ტესტებს; E2E ფრეიმვორკი კი ზემოდან დგას იმ რამდენიმე სრულმარშრუტიანი ტესტისთვის.
დიდი ხნის ნაგულისხმევი არჩევანი JS/TS-ისთვის. ერთი ინსტალაცია ერთად გაძლევთ გამშვებს, მტკიცებებს, მოკირებასა და დაფარვას.
თანამედროვე მოწინააღმდეგე, აშენებული Vite-ის ბანდლერზე. მისი API Jest-ის სარკეა, ამიტომ გადმოსვლა ძირითადად ძებნა-ჩანაცვლებაა.
Java-ს დე ფაქტო სტანდარტი (JUnit 5). ყველა IDE და საბილდე ინსტრუმენტი (Maven, Gradle) მას ნატიურად ხვდება.
Python-ის მთავარი არჩევანი. წერთ უბრალო assert-ს და ის დეტალურ შეცდომის შეტყობინებას თქვენ მაგივრად ქმნის.
Microsoft-ის ბრაუზერის ავტომატიზაციის ინსტრუმენტი. ბრაუზერს გარედან მართავს და ელემენტებს ავტომატურად ელოდება, რაც ტესტებს უფრო მდგრადს ხდის.
თქვენს ტესტს ბრაუზერის შიგნით უშვებს, ყოველი ნაბიჯის ცოცხალი, დროში მოგზაურობის ხედით — უყვართ დეველოპერული გამოცდილების გამო.
ყველაზე საშიში ტესტი ის არის, რომელიც ამჟამინდელ მცდარ ქცევას ამტკიცებს. ის ბაგს "მოთხოვნად" აქცევს, რომლის შეცვლასაც ვერავინ ბედავს. დაამტკიცეთ სასურველი ქცევა.
// the code is wrong; the test "documents" the wrong number test("shipping for 0 items", () => { expect(shippingFee([])).toBe(5) }) // empty carts shouldn't ship at all — but now green // "proves" the bug. Fixing it breaks the suite.
// a failing test that names the intended behavior test("empty cart has no shipping fee", () => { expect(shippingFee([])).toBe(0) }) // red now → fix shippingFee → green. The test is the spec.
რეგრესიული ტესტის წესი: ყოველი ბაგის გასწორება იწყება ტესტით, რომელიც ბაგის გამო ვარდება. ჯერ გაიმეორეთ, მერე გაასწორეთ — რომ ის შეუმჩნევლად ვეღარასდროს დაბრუნდეს.
"ტესტეთ მანამ, სანამ შიში მოწყენილობად არ იქცევა."
— Kent Beck
ხუთი სწრაფი კითხვა პირამიდაზე, AAA-ზე, TDD-ზე, ტესტ-დუბლიორებსა და დაფარვაზე — მყისიერი უკუკავშირი, შესვლის გარეშე.
ნავიგაცია ← → ღილაკებით ან სქროლით · ნაწილი 2: გაღრმავებული ტესტირება → · უკან ბიბლიოთეკაში