Дастурчининг ҳаётида энг кўп учрайдиган вазиятлардан бири шуки, битта кичик ўзгартириш киритасиз ва кутилмаганда сайтнинг бутунлай бошқа қисми ишламай қолади. Бундай ҳолатларнинг олдини олишнинг энг ишончли усули юнит тестдир. Юнит тест — бу коднинг энг кичик мантиқий бўлагини, одатда алоҳида функция ёки методни, қолган тизимдан ажратган ҳолда автоматик равишда текширувчи кичик дастурдир. Сиз функцияга маълум бир кириш қийматини берасиз ва у қандай натижа қайтариши кераклигини олдиндан айтиб қўясиз; агар ҳақиқий натижа кутилган натижага мос келмаса, тест муваффақиятсиз тугайди ва сизни дарҳол огоҳлантиради.
Нега юнит тест ёзиш керак
Юнит тестнинг биринчи ва энг муҳим фойдаси хатони иложи борича эрта тутиб олишдир. Қўлда синаб кўрилганда топилмайдиган мантиқий хатолар автоматик тестлар ёрдамида код ишлаб чиқаришга чиқишидан анча олдин аниқланади, бу эса муаммони тузатиш нархини бир неча баробар камайтиради. Иккинчидан, тестлар кодга ишонч беради: юзлаб тестлар яшил бўлиб турганида, сиз тизимнинг асосий қисмлари ишлаётганига амин бўласиз. Учинчи ва кўпинча энг қадрли фойда рефакторингдир — кодни қайта тузиш жараёнида тестлар ҳимоя тўри вазифасини бажаради, чунки агар сиз бирор нарсани бузиб қўйсангиз, улар дарҳол қизил рангга айланади ва сизга аниқ нима ишламай қолганини кўрсатади.
Асосий тушунчалар: assert ва AAA нақши
Ҳар қандай юнит тестнинг юрагида assertion, яъни тасдиқлаш ётади. Assertion — бу «мен бу қиймат айнан шундай бўлишини кутяпман» деган даъводир; масалан, икки сонни қўшувчи функция 2 ва 3 ни қабул қилиб 5 қайтаришини тасдиқлайсиз. Агар тасдиқлаш бажарилмаса, тест йиқилади. Яхши тузилган тестлар одатда AAA нақшига амал қилади: Arrange (тайёрлаш) босқичида керакли маълумотлар ва объектлар тайёрланади, Act (ҳаракат) босқичида текширилаётган функция чақирилади, ва Assert (тасдиқлаш) босқичида натижа кутилган қиймат билан солиштирилади. Бу уч босқични аниқ ажратиш тестни ўқишни ва кейинчалик уни тушунишни анча осонлаштиради.
PHPUnit билан PHP кодини текшириш
PHP оламида энг кенг тарқалган тест воситаси PHPUnit ҳисобланади. У Composer орқали ўрнатилади ва тестлар одатда алоҳида tests папкасида сақланади. Қуйидаги мисолда биз оддий Calculator классини ва унинг add методи учун тестни кўриб чиқамиз, бунда AAA нақши аниқ кўриниб турибди.
<?php
use PHPUnit\Framework\TestCase;
class Calculator {
public function add(int $a, int $b): int {
return $a + $b;
}
}
class CalculatorTest extends TestCase {
public function testAddReturnsSum(): void {
// Arrange
$calc = new Calculator();
// Act
$result = $calc->add(2, 3);
// Assert
$this->assertSame(5, $result);
}
}
Бу тестни ишга тушириш учун терминалда vendor/bin/phpunit tests/ буйруғини ёзасиз. Агар ҳамма нарса тўғри бўлса, PHPUnit яшил натижа ва ўтган тестлар сонини кўрсатади; методда хато бўлса, у қайси қаторда қайси қиймат кутилгани ва аслида нима қайтганини батафсил кўрсатади.
Jest билан JavaScript функцияларини синаш
Фронтенд ва Node.js лойиҳаларида энг машҳур тест воситаси Jest ҳисобланади. У содда синтаксиси ва тезлиги билан ажралиб туради, ҳеч қандай мураккаб созлашни талаб қилмайди. Қуйидаги мисолда биз JavaScript функциясини ва унинг Jest тестини кўрамиз; бу ерда describe блоки тестларни гуруҳлайди, test функцияси эса алоҳида ҳолатни ифодалайди.
// math.js
function add(a, b) {
return a + b;
}
module.exports = { add };
// math.test.js
const { add } = require('./math');
describe('add funksiyasi', () => {
test('ikki musbat sonni qoshadi', () => {
// Arrange & Act
const result = add(2, 3);
// Assert
expect(result).toBe(5);
});
});
Тестларни npx jest буйруғи билан ишга тушарасиз. Jest барча .test.js файлларини автоматик топади ва уларни бажаради, натижаларни эса ранг билан ажратиб кўрсатади. expect ва toBe жуфтлиги бу ерда айнан PHPUnit'даги assertion вазифасини бажаради.
Мок: ташқи боғлиқликларни алмаштириш
Реал лойиҳаларда функциялар кўпинча маълумотлар базасига, ташқи API'га ёки тўлов тизимига мурожаат қилади. Бундай боғлиқликларни ҳар бир тестда ҳақиқатдан ишга тушириш секин ва ишончсиз бўлади, шу сабабли уларнинг ўрнига мок, яъни сохта нусха қўйилади. Мок — бу ҳақиқий объектга ўхшаб кўринадиган, лекин олдиндан белгиланган натижаларни қайтарадиган назорат остидаги объектдир. Масалан, фойдаланувчини базадан олувчи методни мок қилиб, ҳар доим бир хил тест фойдаланувчисини қайтаришини таъминлайсиз; шунда тест фақат ўз мантиқини текширади, ташқи тизимнинг ҳолатига боғлиқ бўлмайди. Jest'да бу jest.fn() орқали, PHPUnit'да эса createMock() орқали амалга оширилади.
Coverage ва TDD ҳақида қисқача
Code coverage, яъни қамров, тестларингиз коднинг неча фоизини бажариб ўтганини кўрсатувчи ўлчовдир. PHPUnit --coverage-html байроғи билан, Jest эса --coverage билан ушбу ҳисоботни яратади ва қайси қаторлар умуман синалмаганини аниқ кўрсатади. Шуни таъкидлаш керакки, юқори қамров фоизи ўз-ўзидан сифатни кафолатламайди, лекин паст қамров аниқ хавфли зоналарни очиб беради. Test-Driven Development (TDD) эса ёндашув бўлиб, унда сиз аввал тестни ёзасиз, у табиий равишда йиқилади, сўнг уни ўтказадиган минимал кодни ёзасиз ва кейин кодни тозалайсиз. Бу сикл кодни дастлабдан текшириладиган ва содда қилиб лойиҳалашга мажбур қилади.
Қачон ва қандай тест ёзиш керак
Ҳамма нарсани юз фоиз тестлаш шарт эмас ва кўпинча фойдасиздир. Биринчи навбатда бизнес мантиқи мураккаб бўлган, кўплаб шартлар ва ҳисоб-китобларни ўз ичига олган функцияларни тестлаш керак, чунки айнан шу ерда хатолар энг кўп учрайди. Агар сиз бирор хатони топиб тузатган бўлсангиз, ўша хатони қайта келтириб чиқарадиган тест ёзиш жуда фойдали, чунки бу хато келажакда яна пайдо бўлмаслигига кафолат беради. Энг муҳими, тестларни одат тусига айлантиришдир: янги функция ёзганингизда у учун тест ҳам ёзсангиз, вақт ўтиши билан лойиҳангизда ишончли ҳимоя тўри шаклланади ва ҳар бир ўзгартириш анча хотиржам амалга оширилади.