אין פיתוח מקצועי ללא בדיקות אוטומטיות. במקומות אחרים כתבתי על בדיקות אוטומטיות והחשיבות שלהן. במאמר הזה אני אסביר על Mocha שהיא פריימוורק הבדיקות של Node.js והדרך המקובלת בשוק לכתוב בדיקות אוטומטיות ל-Node.js.
את הקוד שאדגים עליו אתם מכירים מהמאמר הקודם שבו דיברתי על המודול. כמו כן הקוד זמין בגיטהאב.
אנחנו נתקין את Mocha שהוא מודול של Node.js באופן גלובלי:
sudo npm install -g mocha
אנו זקוקים להתקנה הגלובלית כי Mocha רץ משורת הפקודה.
אחרי ההתקנה, ניצור תיקיה שנקראת test במודול שלנו. Mocha מריצה את כל קבצי ה-JS שיש בתיקיה זו באופן אוטומטי.
עכשיו בואו ונכתוב את הבדיקה הראשונה. קודם אנו כוללים את כל המשתנים הגלובליים שאנו צריכים. עבור הבדיקה, אנו זקוקים למודול assert:
var assert = require("assert"); // core module
אפשר גם לכלול עוד מודולים ומשתנים. המשתנים והמודולים הם גלובליים ויהיו זמינים לכל מתודות הבדיקה. חשוב להבהיר שזה לא המקום לכתוב setUp (מתודות שרצות לפני סט של בדיקות או לפני כל בדיקה) אלא לעשות require או להגדיר פרמטרים ידניים.
בואו ונראה בדיקה ראשונה:
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
assert.equal(-1, [1,2,3].indexOf(4)); // 4 is not present in this array so indexOf returns -1
})
})
})
הקוד הזה הוא פשוט למדי. בטח למי שמכיר פריימוורקים של בדיקות. באופן עקרוני, כל סט של בדיקות מוקף במתודת describe, כאשר יכולים להיות תת סטים. סט האב הוא בדרך כלל שם המודול שלנו ואז יש עוד תתי סטים שונים לפונקציונליות שונה. במקרה הזה, סט האב הוא array (בדיקות למערך) וסט הבן הוא השם הכללי למגוון הפעולות שאנו בודקים – #indexOf במקרה הזה. בכל מקרה, הטקסט הוא טקסט חופשי וניתן לפי הרצון והצורך.
הבדיקה האמיתית היא במתודה it שכוללת שני פרמטרים, הראשון הוא שם הבדיקה (במקרה הזה האם מקבלים 1- אם מחפשים ערך שלא קיים במערך) והשני הוא פונקציה אנונימית שבה יש assert אחד או יותר.
ה-assert הוא בעצם התנאי. כאן אנו בודקים אם X שווה ל-Y. במקרה הזה אם 1- שווה לחיפוש 4 במערך שיש בו רק 1,2,3.
רוצים דוגמה נוספת? שימו לב לדוגמה הזו מהחיים:
var assert = require("assert"); // core module
var myLog = require('./../lib/logOpener.js');
describe('Simple Log Viewer', function(){
describe('Basic methods and module', function(){
it('should have a createServer Method', function(){
assert.equal(typeof myLog, 'object');
assert.equal(typeof myLog.createServer, 'function');
})
it('should have a addToLog Method', function(){
assert.equal(typeof myLog, 'object');
assert.equal(typeof myLog.addToLog, 'function');
})
it('should have a public log_file_position property after init', function(){
assert.equal(typeof myLog, 'object');
myLog.init(test_log_file);
assert.equal(typeof myLog.log_file_position, 'string');
})
});
});
השם של הסט הכללי של הבדיקות הוא שם המודול שלי, במקרה הזה Simple Log Viewer. השם של תת הסט הוא Basic methods and module. שם אני בודק אם יש לי את 2 המתודות הפומביות של המודול שלי ואת התכונה הפומבית. אם קראתם את המאמר הקודם ו/או הצצתם בקוד של המודול. אתם יודעים שמדובר במתודות :createServer,addToLog ובתכונה: log_file_position
איך אני בודק?
ראשית אני עושה require ל-assert (מודול ליבה) וגם למודול שלי. את ה-require של המודול שלי אני עושה ל: myLog. ועכשיו כל מה שנותר לי לבדוק זה את המתודות כדי לבדוק typeof. במידה וזה function (במקרה של המתודות) או string (במקרה של התכונה) – הכל תקין.
איך בודקים? פשוט כותבים mocha. אם הכל תקין, מקבלים חיווי של כל הבדיקות.
אם אנו רוצים להוסיף פונקציות שירוצו לפני או אחרי כל בדיקה (מה שנקרא setup או teardown), אפשר להוסיף אותן בקלות אפשר להעביר לתוכן callback שיעצרו את כל תהליך הבדיקות עד שהן יושלמו.
למשל, אני רוצה לראות אם באמת השירות שלי יוצר server. לשם כך אני צריך לעשות בדיקה שתבדוק אם אני מקבל מענה בפורט 3000. מה הבעיה? אם אני אכתוב משהו כזה:
describe('Server Operations', function(){
before(function(){
myLog.init(test_log_file, function() {
myLog.addToLog('log item event #1', function() {
myLog.createServer(3000, function() {
});
});
});
});
it('should return 200', function (done) {
http.get('http://localhost:3000', function (res) {
assert.equal(200, res.statusCode);
done();
});
});
});
הבדיקה תרוץ לפני שמתודת ה-createServer תספיק ליצור את השרת. הרי יצירת שרת לוקחת כמה מילישניות לפחות יותר מהרצת הבדיקה. איך אני מוודא שהבדיקות "יחכו" לפונקצית ה-setup? התשובה היא פשוטה – להעביר מתודת done:
describe('Server Operations', function(){
before(function(done){
myLog.init(test_log_file, function() {
myLog.addToLog('log item event #1', function() {
myLog.createServer(3000, function() {
done();
});
});
});
});
it('should return 200', function (done) {
http.get('http://localhost:3000', function (res) {
assert.equal(200, res.statusCode);
done();
});
});
});
זה יכול להיות לא ברור באופן תיאורטי, אבל אתם תזכרו את זה אם יצא לכם לבדוק פרוססים שהם אסינכרוניים.
לסיום, מאוד מקובל ליצור makefile שמסייע לבדיקות. מדובר בקובץ make פשוט:
test:
./node_modules/.bin/mocha --reporter list
.PHONY: test
שימו לב שהרווח שם הוא לא ארבעה רווחים אלא tab. על מנת להריץ את הבדיקות, כל מה שצריך לעשות זה make test.
מאוד מקובל להכניס ל-package.json של המודול את המודולים שהבדיקות צריכות. במקרה שלנו מדובר ב-Mocha כמובן. איך עושים את זה? ככה:
"devDependencies": {
"mocha": "~1.21.4",
"socket.io-client": "~1.1.0"
},
כאשר מישהו ירצה להוריד את גרסת הפיתוח של המודול שלנו, הוא יכול להשתמש ב npm install my-module –dev ולקבל את המודולים של הפיתוח.
יש עוד הרבה מה ללמוד בכל מה שקשור לבדיקות, אבל בגדול זו לא עקומת לימוד תלולה במיוחד. התקנת הסביבה קלה ביותר וגם כתיבת הבדיקות הוא לא משהו שצריך ליפול ממנו. בסופו של דבר, בדיקות טובות יוצרות מודול עמיד יותר שיותר קל לתרום לו.
בגדול? זהו, כאן תם המדריך שלנו בכל הנוגע ל-Node.js. התחלנו בהתקנה פשוטה של סביבת עבודה וסיימנו בכתיבת בדיקות אוטומטיות. כל הידע במדריך הזה לא שווה אגורה בלי תרגול. אני ממליץ לכל מי שהגיע עד לכאן לתרגל, לתרגל ולתרגל – להתחיל לכתוב מודול – לא משנה איזה מודול. הקשיים והפתרונות שתמצאו הם-הם הלמידה האמיתית.