JavaScript: UnitTest ด้วย QUnit กันเถอะ…Action!

เกริ่นนำ

UnitTest ความหมายในที่นี้ คือการทดสอบองค์ประกอบ(components)ของซอฟแวร์ชิ้นหนึ่งที่เราสนใจต้องการพิสูจน์ หรือทดลองวัตถุใดๆภายในองค์ประกอบนั้น ทั้งสถานะ และคุณสมบัติของมัน โดยองค์ประกอบส่วนอื่นที่เกี่ยวข้องกับองค์ประกอบนี้เราจะต้องจำลอง หรือกำหนดสมมุติ สถานะ และคุณสมบัติของพวกมันตามที่เราต้องการได้

QUnit คือเครื่องมือที่ถูกสร้างมาช่วยเรากำหนดค่าสถานะ,  run components, ตรวจสอบผล และแสดงผลของ UnitTest กับองค์ประกอบของเราที่พัฒนาด้วย JavaScripts เท่านั้น ในส่วนของการ ตั้งสมมุติฐานทดสอบส่วนขององค์ประกอบ ส่วนของการจำลอง สมมุติองค์ประกอบอื่นๆที่เกี่ยวข้องกับมัน ยังเป็นสิ่งที่เราต้องทำ และหาเครื่องมืออื่นๆมาช่วยอยู่ เช่น ในการจำลององค์ประกอบอื่นๆภายใต้สมมติฐานที่เราต้องการควบคุม(stub/mock)จะใช้ library JsMockito ช่วยทำเป็นต้น

แนวคิดของการทำงาน UnitTest เพื่อตรวจสอบองค์ประกอบ เป้าหมายที่เราสนใจ ตามสมมุติฐาน หรือความคาดหวังต้องการ หรือที่เราจะต้องได้รับจากองค์ประกอบของเรา ดังนั้น มันจึงมีขั้นตอน ที่เป็นหลักการต้องกระทำอยู่เสมอๆ ในการทำ UnitTest ทุกๆครั้ง เลยก็คือ

  • ตั้งสมมุติฐาน การตั้งสมมติฐาน จะมีการกำหนด ผลที่เราคาดหวังว่าจะต้องได้รับ(expected) กับ การจำกัดขอบเขตสภาวะแวดล้อมต่างๆทั้งหมดที่จะทำให้ components เป้าหมายของเราบรรลุผลเป็นไปตามที่เราคาดหมายไว้ได้ มักจะเรียกขั้นตอนนี้ว่า assign หรือ setup
  • ดำเนินการกระทำกับ components เป้าหมายของเรา ตามสมมุติฐานที่ได้กำหนดไว้แล้ว มักจะเรียกขั้นตอนนี้ว่า action หรือ exercise
  • ตรวจสอบผล และแสดงผล มักจะเรียกขั้นตอนนี้ว่า assert หรือ verify

การตรวจสอบผลที่ถูกต้องตามสมมุติฐานต้องให้ผ่านทั้งหมด แต่ถ้าไม่ถูกต้องทั้งหมดให้กลับไปแก้ไข องค์ประกอบเป้าหมายของเรา แล้วกลับไปเริ่มต้นที่ขั้นตอนที่ 1 ใหม่ วนไปเรื่อยๆจนกระทั้งองค์ประกอบเป้าหมายของเราผ่านทุกๆสมมุติฐานที่เราตั้งไว้ทั้งหมดแล้ว ขั้นตอนก็มีเพียงแค่นี้ เอาละ เรามาเริ่มต้นเขียน UnitTest ด้วย QUnit กันเลย

ทำ UnitTest ด้วย QUnit… Action!

1. สร้าง folder ที่เก็บ project web application ของเราขึ้นมา ตั้งชื่อว่า ScrumStudio  ซึ่งในนี้จะเก็บทุกสิ่งอย่างสำหรับสร้าง web application ของเราทั้งหมด เช่น JavaScript, HTML และ CSS ดังนั้น ให้สร้าง folder ย่อยๆแบ่งโครงสร้างไว้ตามผมไปเลย เป็นโครงสร้างตามผม แบบนี้

\ScrumStudio
	\css
	\scripts
	\test

2. download CSS กับ JavaScript ของ QUnit ได้ที่ qunitjs ซึ่งจะได้ 2 file ที่ชื่อว่า qunit-1.12.0.js กับ qunit-1.12.0.css เอามาแยกเก็บไว้ใน folder ตามผมไปก่อนดังนี้

\ScrumStudio
	\css
		qunit-1.12.0.css
	\scripts
		qunit-1.12.0.js
	\test

3. ถึงขั้นนี้ให้เพิ่ม HTML สำหรับ run test หรือใช้พิสูจน์เนื้อหาในงานของเราเข้าไปก่อน ในที่นี้ผมตั้งชื่อว่า spike-scrum-studiot.html ครับ เพิ่มเข้าไปไว้ที่ folder test มีโครงสร้างแบบนี้

\ScrumStudio
	\css
		qunit-1.12.0.css
	\scripts
		qunit-1.12.0.js
	\test
		spike-scrum-studio.html

4. เปิด file spike-scrum-studio.html ด้วย text editor อะไรก็ได้ แล้วเขียน code ตามข้างล่างนี้ลงไป

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Spike for Scrum Studio</title>
  <link rel="stylesheet" href="./css/qunit-1.12.0.css">
</head>
<body>
  <div id="qunit"></div>
  <div id="qunit-fixture"></div>
  <script src="./scripts/qunit-1.12.0.js" type="text/javascript"></script>
  <script type="text/javascript">
      window.onload = function () {
          test("QUnit setup", function () {
              equal(true, true, "OK");
          });
      }
  </script>
</body>
</html>

จาก code HTML ข้างต้นนั้น ผมได้ include CSS กับ JavaScript สำหรับ QUnit เข้ามาไว้ด้วยแล้วไฮไลท์เป็นสีแดง

ส่วนที่ผม ไฮไลท์เป็นสีส้มนั้นคือ code ส่วนที่อธิบายว่าเราได้ ติดตั้ง QUnit เป็นที่เรียบร้อบแล้วนะ เมื่อแสดงผล HTML นี้ด้วย Chrome หรือ IE จะแสดงผลคล้ายๆผมตามข้างล่างนี้

ก็เป็นอันว่าเราได้ติดตั้ง QUnit เสร็จเรียบร้อยแล้วพร้อมที่จะ UnitTest กันละ

5. ถึงตรงนี้เราก็เริ่มงานกันต่อ สิ่งที่เราจะทำคือ Scrum Studio งั้นเริ่มจาก entity Product กับ ProductBacklog กันเลยก็ละกัน โดยแนวคิด Scrum มาจากที่นี่ ScrumPrimer

\ScrumStudio
	\css
		qunit-1.12.0.css
	\scripts
		\domain-model
		   product.js
		   product-backlog.js
		   qunit-1.12.0.js
	\test
		spike-scrum-studiot.html

6. สำหรับ product.js เขียน JavaScript ตามข้างล่างนี้ลงไป

function Product(properties) {
    properties = properties || {};

    this.Name = properties.Name;
    this.ProductBacklogs = properties.ProductBacklogs;
}

7. สำหรับ product-backlog.js เขียน JavaScripts ตามข้างล่างนี้ลงไป

function ProductBacklog(properties) {
    properties = properties || {};

    this.Priority = properties.Priority;
    this.Item = properties.Item;
    this.Details = properties.Details;
    this.InitialSizeEstimate = properties.InitialSizeEstimate;
}

8. เขียน UnitTest ทดสอบ properties ของ Product กับ ProductBacklog ต่อเลย เปิด file spike-scrum-studio.html แก้ไข code ตรงจุดที่ผมไฮไลท์ข้างล่างนี้เข้าไปครับ

...
  <div id="qunit"></div>
  <div id="qunit-fixture"></div>
  <script src="./scripts/qunit-1.12.0.js" type="text/javascript"></script>
  <script src="./scripts/domain-model/product.js" type="text/javascript"></script>
  <script src="./scripts/domain-model/product-backlog.js" type="text/javascript"></script>
  <script type="text/javascript">
      window.onload = function () {
          test("QUnit setup", function () {
              equal(true, true, "OK");
          });

          test("Check Product Properties", function () {
              var product = new Product({
                  Name: "ScrumStudio"
              });

              equal(product.Name, "ScrumStudio", "Product.Name is ScrumStudio");

              product.ProductBacklogs.push(new ProductBacklog());
              product.ProductBacklogs.push(new ProductBacklog());

              equal(product.ProductBacklogs.length, 2, "Product.ProductBacklogs have 2 Items");
          });

          test("Check ProductBacklog Properties", function () {
              var productBacklog = new ProductBacklog({
                  Priority: 1,
                  Item: "As a ScrumMaster, I want to add a product backlog to my product",
                  Details: "...",
                  InitialSizeEstimate: 10
              });

              equal(productBacklog.Priority, 1, "productBacklog.Priority is 1");
              equal(productBacklog.Item, "As a ScrumMaster, I want to add a product backlog to my product", "productBacklog.Item is OK");
              equal(productBacklog.Details, "...", "productBacklog.Details is OK");
              equal(productBacklog.InitialSizeEstimate, 10, "productBacklog.InitialSizeEstimate = 10");
          });
      }
  </script>
..

เมื่อ run file spike-scrum-studio.html นี้ดูอีกครั้งก็จะแสดงผลแบบนี้

9. ถึงตรงนี้เราจะเห็นว่า สถานะทดสอบเป็นเขียวหมดนั่นหมายความว่า domain model ของเราทั้ง Product กับ ProductBacklog พร้อมไปใช้งานได้แล้ว

สมมุติต่อมา เราได้ค้นพบว่า entity Product มันไม่ถูกต้อง เพราะยังไม่ได้ protect ProductBacklog ที่เพิ่มเข้าไปเลย จาก code เดิมเราเพิ่ม product backlog โดยการเพิ่มไปที่ list ของ product backlog ที่อยู่ใน entity Product ได้โดยตรง ผมจึงกลับไปดำเนินการแก้ไข class Product ที่ file product.js ได้เป็นดังนี้

function Product(properties) {
    properties = properties || {};

    this.Name = properties.Name;

    var productBacklogs = properties.ProductBacklogs || [];

    this.AddBacklog = function (backlog) {
        productBacklogs.push(backlog);
    };

    this.TotalProductBacklog = function () {
        return productBacklogs.length;
    };
}

เมื่อแก้ไข class Product เสร็จแล้วก็ กลับไปแก้ไข UnitTest เพื่อเรียก function ที่เราเพิ่มเข้ามาใหม่ดูอีกที ซิว่า method ที่เพิ่มเข้ามานั้น protect ProductBacklogs และ มันยังคงคุณสมบัติเดิมอยู่รึเปล่า แบบนี้

..
          test("Check Product Properties", function () {
              var product = new Product({
                  Name: "ScrumStudio"
              });

              equal(product.Name, "ScrumStudio", "Product.Name is ScrumStudio");

              equal(product.ProductBacklogs, undefined, "Product.ProductBacklogs have protected");

              product.AddBacklog(new ProductBacklog());
              product.AddBacklog(new ProductBacklog());

              equal(product.TotalProductBacklog(), 2, "Product.ProductBacklogs have 2 Items");
          });
..

เมื่อ run spike-scrum-studio.html อีกทีก็จะแสดงผลใหม่แบบนี้

ก็ขอจบบล็อค UnitTest ด้วย QUnit กันเถอะ…Action! ไว้เพียงเท่านี้นะขอรับ หวังว่าคงจะนำ QUnit ไปประยุกต์ใช้ทดสอบกับงาน JavaScript ของท่านได้เป็นประโยชน์ช่วยเพิ่มความมั่นใจให้กับชิ้นงาน และคุณภาพขององค์ประกอบส่วนหนึ่ง ก่อนปล่อยนำไปประกอบกับชิ้นงานของผู้อื่นเพื่อใช้ประกอบขึ้นมาเป็นซอฟแวร์โปรดักส์ที่สมบูรณ์ใช้ประโยชน์ได้ต่อ

ขอบคุณครับ

#:P

Advertisements

#javascript, #qunit, #unittest

JavaScript: Memoization

Function สามารถเก็บผลลัพย์ที่เคยผ่านการประมวลผลมาก่อนหน้านี้แล้วไว้ที่ object ได้ นี่คือวิธีการเพิ่มประสิทธิภาพที่เรียกกันว่า Memoization

เราลองทำตัวอย่าง function คำนวณอนุกรมแบบ Fibonacci ดู

var times = 0;// ไว้นับจำนวนครั้งที่เรียก function
var fibonacci = function (n) {

++times;
return n < 2 ? n : fibonacci(n – 1) + fibonacci(n – 2);// ที่คือการเขียนแบบ recursive  ปกติ

};

จากตัวอย่าง เรากำหนดตัวแปร times ไว้นับครั้งที่เรียกผ่าน function Fibonacci ไว้ด้วย เมื่อลองเรียกให้คำนวณดูละ

var result = fibonacci(10);
console.log(“fibonacci(10) = ” + result + “, call = ” + times + ” times”);// fibonacci(10) = 55, call = 177 times

จะได้จำนวนครั้งที่เรียก function fibonacci นี้ 177 ครั้ง

แล้วเมื่อเพิ่มประสิทธิภาพด้วยการใช้ Memoization ดู โดยเริ่มจาก เขียน function memoizer เพื่อไว้จดจำผลลัพย์ที่เคยคำนวณแล้วไว้ก่อน

var memoizer = function (memo, formula) {

var recur = function (n) {

var result = memo[n];
if (typeof result !== ‘number’) {

result = formula(recur, n);
memo[n] = result;

}
return result;

};
return recur;

};

นำ function memoizer มาประยุกต์ใช้เข้ากับการคำนวณอนุกรมแบบ fibonacci แบบนี้

var fibonacci = memoizer([0, 1], function (recur, n) {

++times;
return recur(n – 1) + recur(n – 2);

});

เมื่อลองเรียกใช้ดูอีกครั้ง

var result = fibonacci(10);
console.log(“fibonacci(10) = ” + result + “, call = ” + times + ” times”);// fibonacci(10) = 55, call = 9 times

จะเห็นว่าลดเวลาเรียก function เหลือแค่ 9 ครั้งเท่านั้น

ตัวอย่างประยุกต์ใช้ function memoizer กับการคำนวณค่า factorial ได้ด้วย แบบนี้

var factorial = memoizer([1, 1], function (recur, n) {

return n * recur(n – 1);

});

ขอบคุณความรู้จาก

JavaScript: The Good Parts

บันทึกโดย

— #:P —

#javascript

Node.js: ติดต่อฐานข้อมูล mysql

ถ้าอยากที่จะใช้ Node เพื่อติดต่อกับฐานข้อมูล mysql เรามาลงมือทำกันเลย

1. ติดตั้ง Node กันก่อน อ่านบทความ Node.js: แนะนำให้รู้จัก

2. ติดตั้งฐานข้อมูล mysql ให้เครื่องของเรา dowload ได้ที่  http://dev.mysql.com/downloads/mysql

3. ติดตั้ง mysql UI manager เพื่อให้เราจัดการฐานข้อมูล mysql ได้ง่ายขึ้น ผมแนะนำ SQLWave

4. เปิด SQLWave แล้ว connect ไปที่ mysql database ที่เราได้ติดตั้งไว้แล้ว สร้างฐานข้อมูล mydb พร้อมกับสร้าง table products ที่มี field ชื่อว่า Name ให้เราใส่ข้อมูลไว้เป็นตัวอย่างสักสองสามอัน

5. สร้าง folder NodeJs run cmd แล้ว cd ไปที่ folder NodeJs นี้ พิมพ์คำสั่งตามข้างล่างเพื่อติดตั้ง mysql driver module ไว้ใช้ติดต่อฐานข้อมูล mysql ด้วย Node

npm install mysql@2.0.0-alpha3

6. สร้าง file mysql.js ไว้ที่ folder NodeJs แล้วเขียน javascript ด้านล่างนี้ลงไป

var mysql = require(‘mysql’);

var connection = mysql.createConnection({

host : ‘localhost’,
user : ‘ชื่อ login เข้า mysql database ของคุณ’,
password : ‘รหัสผ่านเข้า mysql database ของคุณ’,
database : ‘products’

});

connection.connect();

connection.query(‘SELECT * FROM products’, function(err, results) {

results.forEach(function(product){

console.log(product.name);

});

});

connection.end();

เมื่อทำตามขั้นตอน 1 ถึง 6 เสร็จแล้วเราจะได้โครงสร้างของโปรเจ็คเหมือนภาพข้างล่างนี้

เสร็จละเราก็ run node mysql.js กันได้เลย ซึ่งจะแสดงผลคล้ายๆกับภาพข้างล่างนี้

ข้อมูลเพิ่มเติมของ node mysql ดูได้ที่ https://github.com/felixge/node-mysql

ขอบคุณที่อ่านจนจบครับ

#:P

#javascript, #node

มาสนุกกับการทดลองง่ายๆด้วย JavaScript กันเถอะ… Action!

สวัสดีครับท่านผู้สนใจในงานประดิษฐ์ซอฟแวร์ ที่ได้อ่านอยู่ในขณะนี้ วันนี้ผม จะมาแสดงตัวอย่างการทดลอง หรือการพิศูจน์แนวคิดอะไรก็แล้วแต่ที่เรากำลังให้ความสนใจอยู่ ด้วย JavaScript กันครับ

โดยเริ่มต้นด้วย แนวคิด 7 ขั้นตอนสร้างสิ่งประดิษฐ์แบบ #:P กันก่อน ขั้นตอนทั้ง 7 มีดังนี้

1 สงสัยใคร่รู้ หรือความปราถณาอยากได้ ในสิ่งหนึ่ง (คิด)

2 จินตนาการถึงการมีอยู่ และประโยชน์ที่จะได้รับจากของสิ่งหนึ่งนี้ (จินตนาการ)

3 พยายามสร้างแบบจำลองของ สิ่งหนึ่งนี้ ขึ้นมา (ทำ)

4 ทดลองพิสูจน์ หรือทดลองใช้  แบบจำลองของ สิ่งหนึ่งนี้ดู (ทดลอง)

5 สิ่งหนึ่งนี้ ใช้ประโยชน์ได้ดี นำไปประยุกต์ใช้ในโลกความจริงได้ (ขาย)

6 สิ่งหนึ่งนี้ ใช้การยังไม่ดีพอ กลับไปทำขั้นตอนที่ 3 (ปรับ)

7 สิ่งหนึ่งนี้ ใช้การไม่ได้เลย กลับไปขั้นตอนที่ 1 ใหม่ (สู้ต่อไป)

บทความนี้ผมก็จะเน้นความสนใจ ไปที่ขั้นตอนที่ 4 เลยนะครับ ซึ่งจะเริ่มต้นโดยเขียนชุดคำสั่งสำหรับทดสอบเป็น code JavaScript เพราะผมคิดว่า คุณสามารถเขียน code JavaScript  และทดลอง run มันดูบน browser อย่าง IE หรือ Chrome บนเครื่องของคุณเอง ตามผมมาได้เลยง่ายๆ

เริ่มต้นเขียนเครื่องมือทดสอบ(Test Suit)กันก่อน

ถ้าจะเปลี่ยบชุดทดสอบ ก็จะเปลียบได้กับอุปกรณ์ที่ เสียบเข้ากับชิ้นงานของเรา แล้วมันจะทำงานโดยการใช้งาน หรือ ทดสอบตัวชิ้นงานทั้งหมดให้เองอัตโนมัติ จนกระทั่งหมดกรณีทดสอบที่เราต้องการ แล้วมันก็จะแสดงผลการทดสอบนั้นออกมาให้เราเห็นได้ว่า ชิ้นงานของเราผ่าน หรือไม่ผ่านทุกๆกรณีการใช้หรือยัง

วงจรการทำงานของชุดทดสอบใดๆ กับสิ่งที่เราสนใจอยู่นั้น มีขั้นตอนดังนี้ครับ

1 Setup\Initialize\Given คือ การกำหนดบริบทเริ่มต้นต่างๆให้กับสิ่งที่เราสนใจเพื่อทดสอบ
2 Exercise\Test\When คือ ทำการทดสอบสิ่งที่เราสนใจนี้
3 Verify\Assert\Then คือ เมื่อทดสอบแล้ว สิ่งที่เราสนใจจะต้องได้ผลลัพย์อะไรออกมานั่นเอง

ต่อไปนี้ผมจะแสดงตัวอย่างง่ายๆ ของชุดทดสอบ code JavaScript โดยผมจะ sketch ให้เห็นเป็นลักษณะอุปกรณ์ทดสอบที่เห็นเป็นภาพได้ง่ายๆ เพื่อทดสอบ Math module ทั้งหมดนี้ แต่การสร้างชิ้นงานตาม sketch นั้นจริงๆแล้วเป็น code JavaScript ทั้งหมดเลยนะครับ

ภาพ sketch ข้างล่างนี้เป็นแบบจำลอง ตัวอย่างชุดอุปกรณ์ที่ใช้ทดสอบ Math module ชิ้นงานหนึ่ง

ดูภาพ sketch ประกอบไปด้วย อุปกรณ์หลักๆทั้งหมด 3 ชิ้น อันได้แก่

Test Suit แสดงเป็นรูปสี่เหลี่ยม 3 มิติ่นั่นคือ อุปกรณ์ทดสอบ มี dependency กับ อุปกรณ์เพื่อแสดงผล และ ตัวของชิ้นงาน Math module โดยผ่าน interface
Console แสดงเป็นรูปสี่เหลี่ยมอยู่ด้านขวา นั่นคืออุปกรณ์แสดงผลการทดสอบ ซึ่งจะต้อง implement interface เพื่อแสดงผล แล้วสามารถเชื่อต่อกับตัวอุปกรณ์ได้ interface เพื่อแสดงผล คือ Info และ Error
Math module แสดงเป็นรูปสี่เหลี่ยมอยู่ด้านล่าง เจ้าตัว module นี้จะ implement interface Math ง่ายๆ อันได้แก่ Add, Min, Max และ Pow เพื่อเชื่อมต่อกับ Test Suit ได้

ข้างล่างนี้เป็น code ของอุปกรณ์ Test Suit กับตัวแสดงผล Console ครับ

code Test Suit จะอยู่ใน file test-suite.html

—————————————————————————————————
<!DOCTYPE html>
<html lang=”en”>
<head>
<title>Test-Suite</title>
<style>
#console li.pass {color: Green;}
#console li.fail {color: Red;}
</style>
</head>
<body>
<ul id=”display”></ul>
<script>

var Console = {
           display: null,
           createLi: function (desc, className) {
               var li = document.createElement(“li”);
               li.className = className;
               li.appendChild(document.createTextNode(desc));
               this.display.appendChild(li);
               return li;
           },
           info: function (desc) {
               var li = this.createLi(desc, “pass”);
               return li;
           },
           error: function (desc) {
               var li = this.createLi(desc, “fail”);
               li.parentNode.parentNode.className = “fail”;
               return li;
           }
 };

(function () {

this.verify = function verify(value, desc) {

if (value) {

return Console.info(desc);

}else {

return Console.error(desc);

}

};

this.exercise = function exercise (name, fn) {

Console.display = document.getElementById(“display”);
Console.display = verify(true, name).appendChild(document.createElement(“ul”));
fn();

};

})();

</script>
</body>
</html>
—————————————————————————————————

ตัวอักษรสีส้มๆนั่นคือตัว Console ที่ใช้แสดงผลทดสอบ โดยจะแสดงผลที่ tag HTML UL display บน browser โดยจะแสดงสีแดงกรณีทดสอบไม่ผ่าน(fail) และจะแสดงสีเขียวในกรณี ทดสอบผ่าน(pass)แล้ว ดูที่ function info กับ error ของ Console

ต่อไปเป็น code Math module ที่เป็นตัวชิ้นงานจริงๆกันละครับ module นี้ ก็เปลียบได้กับการผ่านขั้นตอนที่ 1, 2 และ 3 ของหลักการทั้ง 7 มาแล้ว

ต่อไปนี้คือ code JavaScript  ของ math module ง่ายๆ ที่เขียนเสร็จแล้ว หน้าตาเป็นแบบนี้ครับ

code Math module  จะอยู่ใน file math-module.js
—————————————————————————————————

var MathModule = {

add: function add(a, b) {return a + b;},
min: function min(a, b) {if (a <= b) return a;return b;},
max: function max(a, b) {if (a >= b) return a;return b;},
pow: function add(a, b) {return Math.pow(a, b);}

};

—————————————————————————————————

ขั้นตอนสุดท้ายละ เราก็มาทำการทดสอบ Math module กันเลย โดยเปิด file test-suit.html แล้วเสียบปลัก Math module เข้ามาใน Test Suit ครับ พร้อมทั้งเขียน test script เพื่อทดสอบ Math module ลงไปด้วย สำหรับตัวอย่างที่นี้ผมได้เขียน test script ในรูปแบบของ User Stories ครับ

ข้างล่างนี้คือ code test-suit.html ที่เพิ่มการทดสอบตัวชิ้นงานเข้าไปแล้ว

—————————————————————————————————

<!DOCTYPE html>
<html lang=”en”>
<head>
<title>Test-Suite</title>
<style>
#console li.pass {color: Green;}
#console li.fail {color: Red;}
</style>
</head>
<body>
<ul id=”display”></ul>
<script src=”math-module.js”></script>
<script>

var Console = {

display: null,
createLi: function (desc, className) {

var li = document.createElement(“li”);
li.className = className;
li.appendChild(document.createTextNode(desc));
this.display.appendChild(li);
return li;

},
info: function (desc) {

var li = this.createLi(desc, “pass”);
return li;

},
error: function (desc) {

var li = this.createLi(desc, “fail”);
li.parentNode.parentNode.className = “fail”;
return li;

}

};

(function () {

this.verify = function verify(value, desc) {

if (value) {

return Console.info(desc);

}else {

return Console.error(desc);

}

};

this.exercise = function exercise(name, fn) {

Console.display = document.getElementById(“display”);
Console.display = verify(true, name).appendChild(document.createElement(“ul”));
fn();

};

})();

  window.onload = function () {
           exercise(“As a Math Module, I want to add a and b so that I have a plus b.”, function () {
               verify(MathModule.add(2, 3) == 5, “SCENE-1: Given 2 and 3 when I call method add then I have 5.”);
               verify(MathModule.add(-1, -9) == -10, “SCENE-2: Given -1 and -9 when I call method add then I have -10.”);
           });
           exercise(“As a Math Module, I want to find minimum value a and b so that I have minimum value.”, function () {
               verify(MathModule.min(6, 2) == 2, “SCENE-1: Given 6 and 2 when I call method min then I have 2.”);
           });
           exercise(“As a Math Module, I want to find maximum value a and b so that I have maximum value.”, function () {
               verify(MathModule.max(10, 5) == 10, “SCENE-1: Given 10 and 5 when I call method max then I have 10.”);
           });
           exercise(“As a Math Module, I want to find power value a raised to power b so that I have power value.”, function () {
               verify(MathModule.pow(2, 2) == 4, “SCENE-1: Given 2 and 2 when I call method pow then I have 4.”);
               verify(MathModule.pow(4, 0.5) == 2, “SCENE-2: Given 4 and 0.5 when I call method pow then I have 2.”);
           });
       };

</script>
</body>
</html>

—————————————————————————————————

ตรงตัวอักษรสีแดงคือการอ้างอิง หรือเสมือนกับการปลัก math module ที่เป็นชิ้นงานเป้าหมายเพื่อทดสอบ เข้าไปกับตัวอุปกรณ์ทดสอบ(Test Suit) ของผมนั่นเองครับ

ส่วนตัวอักษรสีเขียวๆนั่นก็คือ test script ที่เขียนด้วย code JavaScript ในรูปแบบของ User Stories ครับ

เมือลอง run file Test Suit บน browser(ผมใช้ Chrome) ขึ้นมาดูตัว Test Suit จะแสดงผลทดสอบของผม ออกมาตามหน้าจอข้างล่างนี้เลยครับ

OK จบกันแค่นี้ละครับ สำหรับบทความ มาสนุกกับการทดลองง่ายๆด้วย JavaScript กันเถอะ… Action!

บทความนี้ เขียนแล้ว อยากจะให้มันสั้นๆ เข้าไว้นะครับ มันจึงเป็นบทความที่ เต็มไปด้วยความรู้ที่ซ่อนอยู่ และความไม่รู้ของผมที่ทำให้ผมเข้าใจผิดพลาดได้ ซึ่งมันอาจจะทำให้คุณผู้อ่านไม่เข้าใจได้ งงๆได้ ก็ส่งคำถามมาคุยกันได้นะครับ ผมจะได้รู้ และกลับมาแก้ไขบทความของผมนี้ให้ดีขึ้นต่อไป

ขอบคุณครับ
#:P

เพิ่มเติม:

  • ถ้าคุณเริ่มเข้าใจการทำงาน ของเจ้าตัวทดสอบนี้แล้ว ก็ไม่จำเป็นต้องเขียน Test Suit แบบนี้ขึ้นมาเอง เป็นสิ่งที่ดีกว่าโดยหยิบยืมของคนอื่นที่ทำดีอยู่แล้วมาใช้ ผมแนะนำ QUnit
  • Code Test Suit ปรับมาจากหนังสือ JavaScript Ninja

 

#agile-software-development, #javascript, #unit-test

JavaScript: มารู้จัก method apply กับ call ของ function กันเถอะ

ในภาษา JavaScript หลายๆคนก็คงจะรู้จัก method apply และ call ของ function กันแล้ว และคงรู้ดีอยู่แล้วว่า function นั้นก็คือ first-class objects ของ JavaScript บทความนี้ก็มาทบทวนกันหน่อยก็แล้วกันครับ

method apply และ call คือวิธีการเรียก function วิธีหนึ่งโดย ทั้งคู่จะรับ input หรือมี interface รับสองค่า คือ function context และ arguments จะต่างกันก็แค่ method apply รับ parameter เป็น array โดยที่ method call รับ arguments เป็นค่าเรียงกันไปตรงๆเลย ดูตัวอย่าง code JavaScript ข้างล่างนี้เลย

function sum () { //ประกาศ function sum

var result = 0;

//วน loop ใน arguments เพื่อรวมค่า แล้วทดไว้ที่ตัวแปร result
for (var i = 0; i < arguments.length; i++) {

result += arguments[i];

}
this.result = result;//เก็บผลรวม result ไว้ที่ function context นี้

}

var ninja1 = {}; /* setup object ninja1 เป็น function context เตรียมไว้ทดสอบ method apply */
var ninja2 = {}; /* setup object ninja2 เป็น function context เตรียมไว้ทดสอบ method call */

/* เรียก method apply โดยกำหนด arguments เป็น array object [1, 2, 3, 4] */
sum.apply(ninja1, [1, 2, 3, 4]);

/* เรียก method call โดยกำหนด arguments เรียงต่อๆกันไปคือ 1, 2, 3, 4 */
sum.call(ninja2, 1, 2, 3, 4);

/* alert เพื่อดูผลซึ่งจะได้ค่า result เท่ากับ 10 */
alert(“sum.apply(ninja1, [1, 2, 3, 4]) = ” + ninja1.result + “, sum.call(ninja2, 1, 2, 3, 4) = ” + ninja2.result);

เรามาดูกรณีการใช้ วิธีการเรียก method แบบนี้ ในตอนนำไปประยุกต์ใช้กับ function แบบ Callback กันอีกหน่อย

code JavaScript ข้างล่างนี้คือการประกาศ function forEach ซึ่งจะทำงานเรียก function callback กับ list ที่เราใส่เข้าไป โดย interface ของ forEach จะรับ list ของ function context ที่สนใจ กับ function callback ที่แสดงเป็นพฤติกรรมที่เราต้องการให้เรียกกลับไปยัง function context ที่สนใจนั้น

function forEach (list, callback) {//ประกาศ function forEach

for (var i = 0; i < list.length; i++) {//วน loop เข้าไปใน list

/* เรียก method callback ในแบบ method call ของ function โดยใส้ function context เป็นสมาชิกใน list ตัวที่ i และส่ง parameter 1 ตัวเป็น index ของ list */
callback.call(list[i], i);

}

}

var list = [‘shuriken’, ‘katana’, ‘nunchucks’];//setup ค่า list เป็น array ของ string

/* เรียก function forEach โดยส่ง list ของ function context เป็น object string กับ function callback โดยจะ alert ค่า index กับ ตัวสมาชิกใน array list ออกมาเฉยๆ */
forEach(list, function (index) {

alert(“index: ” + index + “, this: ” + this);

});

ก็ขอจบไว้เพียงเท่านี้ หากท่านใดกำลังเขียน code JavaScript อยู่ก็คงจะได้ใช้ประโยชน์จากบทความนี้บ้างอย่างน้อยก็ยังดีนะครับ

แหล่งความรู้: Secrets of the JavaScript Ninja

ขอบคุณครับ

#:P

#javascript

Unobtrusive JavaScript

Unobtrusive JavaScript

Unobtrusive JavaScript ก็คือหลัการ(principle) ช่วยจัดการ code ที่จะทำให้ เอกสาร markup HTML ของเรานั้น ไร้ซึ่ง logic จาก code JavaScript

หลักการนี้ก็คล้ายๆกับการที่เราเอา style ออกจากเอกสาร markup HTML ได้โดยเขียน CSS นั้นเอง

ดูตัวอย่างกันเลยดีกว่า

ตัวอย่าง markup HTML ก่อนใช้ Unobtrusive JavaScript

ข้างล่างนี้เป็น code ใน file เอกสาร markup HTML ของ App น่ารักๆชิ้นหนึ่ง

App น่ารักๆ ทำงานง่ายๆ คือเมื่อผู้ใช้กรอกข้อมูลใน textbox แล้วกดปุ่ม  Alert Si! ก็จะทำให้เกิด event onclick ไปเรียก  function alertSi ที่จะเอาข้อความที่ผู้ใช้กรอกไว้ใน textbox แล้ว alert ขึ้นมาที่ browser ก็แค่นั้น

//file alertSi.html

<!DOCTYPE html>
<html lang=”en”>

<head>

<title>Alert Si!</title>

<script src=”alertSi.js”></script>

</head>
<body onload=”wireUp()”>

<input id=”txt-message” type=”text” />
<input id=”cmd-alert-si” type=”button” value=”Alert Si!” onclick=”alertSi()”/>

</body>

</html>

จุดที่ผมได้ highlight สีแดงไว้นั้นคือ code  javascript ที่รวมอยู่ในเอกสาร markup HTML นั่นเอง ข้างล่างนี้เป็น code javascript ส่วนของ logic งาน init และ alertSi 

//file alertSi.js

(function () {

var txtMessage;
this.wireUp = function () {

txtMessage = document.getElementById(“txt-message”);

};
this.alertSi = function () {

alert(txtMessage.value);

};

})();

เอาละครับเรามาเริ่มใช้หลักการของ Unobtrusive JavaScript กันเลย

ล้าง markup HTML ให้สะอาดด้วย Unobtrusive JavaScript

1. เอา code javascript ที่ผม highlight สีแดง ไว้ออกจาก file alertSi.html ครับ จะได้ เอกสาร markup HTML ใหม่แบบนี้

<html lang=”en”>

<body>

<input id=”txt-message” type=”text” />
<input id=”cmd-alert-si” type=”button” value=”Alert Si!” />

</body>

</html>

2. ขั้นตอนนี้ผมจะต้องทำการผูก event ให้เอกสาร markup HTML  ได้แก่ onload และ onclick ที่ได้ลบออกไปแล้ว และเพื่อทำให้ App น่ารักๆนี้ ทำงานได้เหมือนเดิม ผมจะเขียน code JavaScript เพื่อผูก event onload และ onclick เพิ่มเติมลงไปใน  file alertSi.js แบบนี้

(function () {

var txtMessage;
var cmdAlertSi;

this.wireUp = function () {

txtMessage = document.getElementById(“txt-message”);

cmdAlertSi = document.getElementById(“cmd-alert-si”);

//Binding event กับ command
cmdAlertSi.onclick = function () {

alertSi();

};

};

this.alertSi = function () {

alert(txtMessage.value);

};

})();

window.onload = wireUp;

เพียงเท่านี้เราก็จะได้ เอกสาร markup HTML ที่สะอาดๆ ไร้ซึ่ง logic JavaScript แล้วด้วยการใช้หลักการ Unobtrusive JavaScript ครับ 😉 …

ขอบคุณครับ

–#:P–

#javascript

HTML 5 Canvas: วาดข้อความ 3D

เกริ่นนำ

การวาด text แบบ 3D ใน HTML Canvas นั้นทำไม่ได้ แต่บทความนี้ผมจะใช้ trick เพื่อวาด ข้อความในแบบ 3D โดยไม่ใช่รูปภาพ หรือ content 3D ใดๆเลย หน้าตาของ web เมื่อทำเสร็จแล้วจะแสดงออกมาแบบนี้

ลงมือทำ… Action!

1. สร้าง HTML file แล้วตั้งชื่อว่า 3DText.html บันทึกไว้ที่ไหนก็ได้ครับ

2. เปิด file HTML จากข้อ 1 ขึ้นมาเขียน code เริ่มต้นเอกสาร HTML  ตามข้างล่างนี้ลงไป

<!DOCTYPE HTML>

<html>

<head>
</head>

<body>

<canvas id=”myCanvas” width=”600″ height=”250″ style=”border:1px solid black;” />

</body>

</html>

3. เปิด tag javascript ครับแล้วเติม function draw3dText เพื่อวาด 3D text ให้เรา ตาม code ข้างล่างนี้

<script>

//context: บริบท 2D ของ Canvas
//text: ข้อความที่ต้องการแสดงเป็น 3D
//x: ตำแหน่งเริ่มต้นของ text ตรงพิกัด x ใน canvas
//y: ตำแหน่งเริ่มต้นของ text ตรงพิกัด y ใน canvas
//textDepth: ความลึกในเงา(Shadows) ของ text
var draw3dText = function (context, text, x, y, textDepth) {

var n;

//วาด layer ล่างของ text เป็นชั้นๆจากล่างขึ้นบน

//เท่ากับความลึกที่ต้องการ(textDepth)

for (n = 0; n < textDepth; n++) {

context.fillText(text, x – n, y – n);

}

//วาด layer บนสุด กำหนดสีและแสดง shawdows ไว้ที่ layer ล่างสุด
context.fillStyle = “#5E97FF”;
context.shadowColor = “black”;
context.shadowBlur = 10;
context.shadowOffsetX = textDepth + 2;
context.shadowOffsetY = textDepth + 2;
context.fillText(text, x – n, y – n);

}

</script>

4. ขั้นตอนต่อไป ผมจะวาด 3D text ใน myCanvas โดยใช้ function draw3dText ซึ่งผมจะวาด 3D text ไว้ที่ขั้นตอน onload ของ window  เติม code ต่อจาก function draw3dText  ตามข้างล่างนี้

window.onload = function () {

var myCanvas = document.getElementById(“myCanvas”);
context = myCanvas.getContext(“2d”);

context.font = “40pt Calibri”;
context.fillStyle = “black”;

//กำหนด text ใน myCanvas ให้อยู่ตรงกลางในแนวนอน
context.textAlign = “center”;

//กำหนด text ใน myCanvas ให้อยู่ตรงกลางในแนวตั้ง
context.textBaseline = “middle”;

draw3dText(context, “สวัสดี Text 3D เหลา”, myCanvas.width / 2, 120, 5);

}

5. เสร็จแล้วละครับ ลอง run ทดสอบดูได้เลย(test drive)

HTML5 Canvas ทำงานยังไง

หลักการก็คือ ผมวาด text 3D ด้วยการวาด text 2D ธรรมดาๆนั่นละครับ โดยวาด text ปกติที่จะแสดง ไว้บนสุดให้กำหนดสีตามต้องการ แล้ววาด text เดิมซ้ำ ซ้อนกันเป็นขั้นๆ เพื่อแสดงความลึกของ text โดยใช้สีดำ หรือสีของ text แต่เข้มกว่าก็ได้

แล้วตบท้ายด้วยการให้ เงา(shadows) แต่ให้ระบุ Offset x และ y หรือจุดเริ่มต้นที่จะวาดเงา เพื่อที่จะได้วาดแสงเงาไปไว้ที่ความลึกของ text ให้แลดูว่าเป็นข้อความที่มีลึก ที่มีเงาให้สมจริงนั่นเอง(real world)

เข้าไปดู function draw3dText ในตัวอย่างของผมกัน ผมวาด text “สวัสดี Text 3D เหลา” นี้ซ้ำๆกันทั้งหมด 5 layers เป็นความลึก ขั้นนี้ text จะเป็นสีดำ เพื่อแสดงถึงความหนาของ text ที่ทึบแสง

หลังจากที่วาด text ความลึกเสร็จแล้ว ผมก็กำหนดสีของ text บนสุดเป็น #5E97FF

และในขั้นตอนสุดท้ายผมก็วาด เงาบางๆโดยกำหนด shadowColor, shadowBlur, shadowOffsetX และ shadowOffsetY ตามต้องการ

ผมขอจบบทความ HTML 5 Canvas: วาดข้อความ 3D ไว้เพียงเท่านี้ครับ คุณสามารถกลับไปอ่านบทความที่เกียวข้องเพิ่มเติมของผม ได้จาก HTML5 Canvas: แนะนำให้รู้จักด้วยการวาดเส้นตรง 2D ต่อเนื่องกันเป็นรูป Zigzag

 

แหล่งข้อมูล: HTML5 Canvas Cookbook

ขอบคุณครับ

#:P

#html5, #javascript