آموزش جاوا اسکریپت – جلسه ۲۶ – تست نویسی [Testing]

درود بر کاربران عزیز و علاقه مندان به زبان جاوااسکریپت. در جلسه قبل (بیست و پنجم) سوالات متداولی که در مصاحبه های استخدام برنامه نویس فرانت اند (به ویژه جاوا اسکریپت) مطرح می شوند را بررسی کردیم. در این جلسه قصد داریم با آموزش نحوه تست نویسی و معرفی ابزارهای آن در javascript در خدمت شما باشیم.
وقتی مقیاس پروژه یا توابعی که توسط javascript نوشته شده اند بزرگ می شود، استفاده از تست نویسی توصیه می شود. بوسیله تست نویسی می توانیم خروجی فعلی نرم افزار را با خروجی مورد انتظار (Expected) مقایسه کنیم و در صورت بروز خطا (Fail شدن تست) به بررسی و رفع مشکل بپردازیم.
انواع تست نویسی و ابزارهای آنها:
بطور کلی سه نوع تست داریم:
- Unit Test: معمولا یک قسمت خاص از نرم افزار تست می شود. دقت تست در این حالت زیاد است و همینطور تعداد تست ها نیز بیشتر از حالات دیگر است. مثل تست تابع.
- Integration Test: این نوع تست بر اساس وابستگی ها یا dependencies انجام می شود. مثلا تابعی را تست می کنیم که در داخل آن توابع دیگر در حال فراخوانی (call) هستند.
- End-to-End Test: وقتی می خواهیم ارتباط با کاربر را در یک نرم افزار تست کنیم از تست E2E استفاده می شود. مثلا اعتبارسنجی DOM. فرض کنید وقتی کاربر روی یک دکمه کلیک می کند انتظار داریم چه اتفاقی بیوفتد.
برای انجام این تست ها ابزارهایی وجود دارد که تست ها بتوانند بصورت منطقی تر، ساده تر و اتوماتیک انجام شوند.
برای تست نویسی جاوا اسکریپت در دو حالت اول (unit test و integration test) می توان از فریم ورک Jest استفاده کرد. این فریم ورک قدرتمند می تواند برای تست کتابخانه ها و فریم ورک های جاوا اسکریپتی زیر مورد استفاده قرار گیرد:
Babel, TypeScript, Node, React, Angular, Vue
اما برای تست نویسی End to End از کتابخانه Puppeteer استفاده کرد. روش کار این ابزار به این صورت است که مرورگر گوگل کروم را برای ما شبیه سازی می کند و می توان ارتباط کاربر با نرم افزار تحت وب را شبیه سازی و تست کنیم.
آماده سازی محیط پروژه:
یک فولدر با نام دلخواه، مثلا testing-project ایجاد کرده و فایل index.html را با محتوای زیر در آن ایجاد کنید:
محتوای فایل index.html
<!DOCTYPE html> <head> <link rel="stylesheet" href="./styles/bootstrap-grid.min.css"> <link rel="stylesheet" href="./styles/styles.css"> <title>Testing</title> </head> <body> <div class="header"> <div class="container"> <h1>Add Product</h1> </div> </div> <div class="container main"> <div> <input type="text" id="title" placeholder="Title..."> </div> <div> <input type="text" id="price" placeholder="Price..."> </div> <button id="add-product">Add</button> </div> <div class="products"> <ul></ul> </div> <script src="dist/main.js"></script> </body> </html>
سپس یک فولدر با نام src ایجاد کرده و و دو فایل js بنام های index.js و util.js در آن تعریف کنید. محتوای این فایل ها بصورت زیر هستند:
محتوای فایل util.js
exports.generateText = (title, price) => { return `${title} ${price}` } exports.createElement = (type, text, className) => { const newElm = document.createElement(type); newElm.classList.add(className); newElm.textContent = text; return newElm; }
در این فایل دو کار انجام می شود: ایجاد المنت لیست یا li که توسط تابع createElement انجام می گیرد. و تولید متن داخلی المنت (نام محصول + قیمت) که تابع generateText انجام می دهد.
محتوای فایل index.js
const { generateText, createElement } = require('./util'); const initApp = () => { const btnNewProduct = document.querySelector("#add-product"); btnNewProduct.addEventListener('click', addProduct) } const addProduct = () => { const productTitle = document.querySelector('#title'); const productPrice = document.querySelector('#price'); const products = document.querySelector('.products'); const titleValue = productTitle.value; const priceValue = productPrice.value; const output = generateText(titleValue, priceValue); const productEl = createElement('li', output, 'product-list'); products.appendChild(productEl); } initApp();
نصب و تنظیمات وب پک:
در پروژه ای که می خواهیم تست کنیم نیاز به یک Module Bundler بنام وب پک داریم. پس نیاز است آن را در پروژه خود نصب کنیم.
در نرم افزار ویژوال استودیو کد، ترمینال را باز کرده و دستورات زیر را در npm اجرا کنید:
Npm install –save webpack webpack-cli
در فایل package.json فرمان start را به شکل زیر تعریف کنید:
“start”: “webpack –watch”
نکته 1: برای اجرای پروژه بصورت لایو نیاز است Live Server در نرم افزار ویژوال استودیو کد شما نصب باشد.
نکته 2: برای یادگیری وب پک (WebPack) بصورت کاربردی و جامع، به وب سایت آکادمی محتوابان مراجعه کنید.
فایل ها و فولدرهای زیر را ایجاد کنید:
ما در این پروژه یک تکست باکس داریم برای درج نام محصول و یک تکست باکس داریم برای قیمت آن و یک دکمه افزودن. با کلیک روی این دکمه، محصول بهمراه قیمتش در لیست موردنظر چاپ می شود.
در این فایل تمام المنتهای صفحه html را دریافت کرده ایم و در متغیر تعریف کردیم و با کلیک روی دکمه افزودن، المنت محصول بصورت تگ li در div مربوط به محصولات درج می شود.
اجرای تست یونیت (Unit Test) توسط JEST:
حال می خواهیم این توابع توسط JEST را تست کنیم.
نصب فریم ورک jest:
اجرای دستور npm install –save jest
ایجاد یک فایل بنام util.test.js در فولدر src و تعریف کدهای زیر در آن:
const { generateText } = require('./util'); test('output title and price', () => { const text = generateText('Book', 29); expect(text).toBe('Book 29'); })
در این فایل ابتدا تابعی را که می خواهیم تست کنیم را ایمپورت کرده ایم سپس تابع test را از فریم ورک jest فراخوانی کرده و بعنوان آرگومان اول یک نام دلخواه برای تست تعریف می کنیم و بعنوان آرگومان دوم بدنه تست را می نویسیم. در این مثال گفته ایم انتظار داریم اگر آرگومان های ورودی تابع generateText برابر Book و 29 باشند، خروجی کار برابر Book 29 باشد.
در فایل package.json تعریف فرمان تست jest بصورت زیر در بخش scripts:
"test": "jest"
سپس با مراجعه به cmd و اجرای دستور npm test می توانیم تست نرم افزار خود را توسط jest اجرا کنیم.
مشاهده می شود که تست با موفقیت انجام شده است:
حال اگر در فایل util تابع generateText را به صورت زیر تعریف کنیم، تست با خطا مواجه می شود:
exports.generateText = (title, price) => { return `${price} ${price}` }
اکنون دستور npm test را اجرا کنید. خروجی زیر را خواهیم داشت:
مشاهده می شود که خروجی مورد انتظار (Expected) برابر Book 29 است اما خروجی بدست آمده از تابع 29 29 است. پس تست با خطا مواجه شده است.
اجرای تست یکپارچه سازی (Integration) توسط JEST:
برای اجرای تست integration در مثال فوق باید کمی تغییرات در کدمان ایجاد کنیم و آن را ماژولار کنیم.
آماده سازی پروژه برای اجرای تست integration:
بدین منظور می خواهیم برای ورودی های title و price اعتبارسنجی یا validation در نظر بگیریم. مثلا فیلد price باید از نوع عددی باشد و خالی نباشد.
توابع زیر را به فایل util.js اضافه کنید:
const validateInput = (text, notEmpty, isNumber) => { if (!text) { return false; } if (notEmpty && text.trim().length === 0) { return false; } if (isNumber & text === NaN) { return false; } return true; } exports.validateAndGenerate = (title, price) => { if (!validateInput(title, true, false) || !validateInput(price, false, true)) { return false; } return generateText(title, price); } exports.generateText = generateText; exports.validateInput = validateInput;
همچنین در تابع generateText باید کلمه exports را حذف کنیم و از constاستفاده کنیم:
const generateText = (title, price) => { return `${title} ${price}` }
زیرا می خواهیم کارهای validate و generate را روی تکست ورودی در یک تابع بنام validateAndGenerate انجام دهیم.
اکنون در فایل index.js تغییرات زیر را اعمال کنید. در ابتدای فایل ایمپورت را بصورت زیر تعریف کنیم:
const { validateAndGenerate, createElement } = require('./util');
و متغیر output را به شکل زیر:
const output = validateAndGenerate(titleValue, priceValue); if (!output) return;
شرط if را به این دلیل نوشتیم که اگر خروجی نداشتیم اجرای برنامه متوقف شود. حالا برنامه ما ماژولار شده است و آماده تست Integrationمی باشد.
اجرای تست Integration:
در فایل util.test.js ایمپورت را به صورت زیر تعریف کنید:
const { generateText, validateAndGenerate } = require('./util');
و یک تابع تست جدید مطابق کد زیر بنویسید:
test('check and generate input', () => { const text = validateAndGenerate('Book', 79); expect(text).toBe('Book 79'); });
اکنون اگر دستور npm test را اجرا کنیم هر دو تست ما اجرا می شوند.
اما اگر شرط NOT را در دستور if تابع validate حذف کنیم، تست ما با خطا مواجه خواهد شد:
اجرای تست End to End توسط کتابخانه Puppeteer:
تست E2E در واقع برای تست رابط کاربری نرم افزار (یا ارتباط کاربر با نرم افزار) بکار می رود. همانطور که قبلا گفتیم برای این نوع تست از کتابخانه Puppeteer استفاده می کنیم. بدین منظور ابتدا باید لایبرری پاپتیر را نصب کنیم.
نصب کتابخانه Puppeteer:
اجرای دستور npm install –save puppeteer
بطور کلی می توان گفت puppeteer مرورگر و همچنین رفتار کاربران را شبیه سازی می کند. مانند یک ربات!
مثلا می توان تعریف کرد که puppeteer کلیک کاربر روی input، تایپ کردن متن و کلیک روی دکمه افزودن را شبیه سازی کنید.
آماده سازی پروژه برای اجرای تست End To End:
برای شروع کار فایل util.test.js را باز کنید و کد زیر را در ابتدای فایل اضافه کنید:
const puppeteer = require('puppeteer');
سپس تابع test را به شکل زیر تعریف کنید:
test('clicked', async() => { const browser = await puppeteer.launch({ headless: false, slowMo: 80, args: ['--window-size=1920,1080'] }); const page = await browser.newPage(); await page.goto('http://127.0.0.1:5500/'); await page.click('#title'); await page.type('#title', 'Book'); await page.click('#price'); await page.type('#price', '79'); await page.click('#add-product'); });
بعنوان اولین آرگومان تابع test نام دلخواه clicked را در نظر گرفتیم. سپس یک تابع ناهمگام async تعریف کرده ایم. در این تابع ابتدا یک متغیر با نام دلخواه browser تعریف کرده ایم که برابر اجرای تابع launch در کتابخانه puppeteer می باشد. آرگومان ورودی launch یک آبجکت است که شامل headless و slowMo و args است. Headless باعث می شود که مرورگر شبیه سازی شود. SlowMo باعث می شود رفتار کاربر با یک تاخیر اجرا شوند که راحت تر قابل مشاهده باشد. در آرایه args هم ابعاد مرورگر (مثلا 1920 * 1080) تعریف شده است.
سپس متد newPage را از آبجکت browser اجرا می کنیم که کارش ایجاد یک صفحه جدید در مرورگر است. حالا روی این صفحه کارهای زیر را به ترتیب انجام می دهیم تا رفتار کاربر را شبیه سازی کرده باشیم:
- رفتن به آدرس 0.0.1:5500
- کلیک روی المنت با آیدی title
- تایپ کلمه Book در آن
- کلیک روی المنت با آیدی price
- تایپ کلمه 79 در آن
- کلیک روی دکمه افزودن محصول
تست API در جاوا اسکریپت:
زمانیکه می خواهیم در نرم افزار خود داده ها را از api دریافت کنیم، می توان از این روش برای تست نرم افزار استفاده کرد.
API ای می خواهیم در این مثال استفاده کنیم Fake است و از سایت jsonplaceholder استفاده می کنیم. برای کار با api می توان از fetch یا axios استفاده کرد. ما در این مثال می خواهیم از axios برای ارتباط با api استفاده کنیم. پس باید کتابخانه axios را نصب کنیم.
برای نصب axios دستور npm install –save axios را اجرا کنید.
سپس یک فایل با نام دلخواه http ایجاد کرده و کدهای زیر را در آن تعریف کنید:
const axios = require('axios'); const getData = () => { return axios .get("https://jsonplaceholder.typicode.com/todos/1") .then(res => res.data); } exports.getData = getData;
در کدهای فوق توسط axios به fake api خود دسترسی پیدا کردیم و دیتای موردنظر را دریافت کرده ایم و در نهایت در getData آن را اکسپورت کرده ایم.
حالا در فایل util.js کدهای زیر را اضافه کنید:
// API testing const { getData } = require('./http'); const loadData = () => { return getData().then(data => { const title = data.title; return title; }) } const outputTitle = () => { loadData().then(title => { console.log(title); }) } exports.loadData = loadData; exports.outputTitle = outputTitle;
سپس در فایل index.html دکمه زیر را تعریف کنید:
<button id="get-data">Get Data</button>
در مرحله بعد در فایل index.js کدهای زیر را تعریف کنید:
// API testing const { outputTitle } = require('./util'); const btnGetData = document.getElementById("get-data"); btnGetData.addEventListener('click', outputTitle);
حالا در فایل util.test.js می خواهیم تابع loadData را تست کنیم. کدهای زیر را در این فایل اضافه کنید:
const { loadData } = require('./util'); test('load test', () => { loadData().then(title => { expect(title).toBe('delectus aut autem'); }) })
حال دستور npm test را اجرا کنید. خروجی تست موفقیت آمیز خواهد بود:
مشاهده می کنید که هر 3 تست ما در این مثال PASS شده اند:
Output title and price
Check and generate input
Load test
مقاله آموزشی تست نویسی در جاوا اسکریپت و ابزارهای آن در اینجا به پایان می رسد. در مقاله بعد می خواهیم مبحث ماژول ها (Module) را در جاوا اسکریپت (Ecma Script 6) بطور کامل و کاربردی بررسی کنیم و نحوه import و export کردن ماژول را به همراه مثال های متعدد آموزش دهیم.
دیدگاهتان را بنویسید