<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Continuous Integration &#8211; Few Steps &#8211; ก้าวสั้นๆ แต่ไปเรื่อยๆ</title>
	<atom:link href="https://myifew.com/tag/continuous-integration/feed/" rel="self" type="application/rss+xml" />
	<link>https://myifew.com</link>
	<description></description>
	<lastBuildDate>Tue, 17 Jul 2018 10:08:14 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://myifew.com/wp-content/uploads/2018/07/cropped-logo6-ts-32x32.png</url>
	<title>Continuous Integration &#8211; Few Steps &#8211; ก้าวสั้นๆ แต่ไปเรื่อยๆ</title>
	<link>https://myifew.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>ต้องคิดอย่างไร รู้อะไร และทำอย่างไร เพื่อให้เกิด Automated Test</title>
		<link>https://myifew.com/4455/design-and-develop-code-for-testable/</link>
					<comments>https://myifew.com/4455/design-and-develop-code-for-testable/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Tue, 10 Apr 2018 09:01:02 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Automation Testing]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Integration Test]]></category>
		<category><![CDATA[Test Strategy]]></category>
		<category><![CDATA[Testable]]></category>
		<category><![CDATA[Unit Test]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=4455</guid>

					<description><![CDATA[หลังจากได้เขียนอธิบายเรื่อง "จะทำระบบให้รองรับ Automated Test ได้อย่างไร (Testable)" ไปคราวก่อน ได้รับผลตอบรับด้วยดี คราวนี้เลยมาเขียนเพิ่มเติมเพื่อเป็นตัวอย่างแก่ผู้ที่ต้องการนำไปพัฒนาระบบจริงครับ]]></description>
										<content:encoded><![CDATA[<p>หลังจากได้เขียนอธิบายเรื่อง &#8220;<a href="https://myifew.com/4414/design-project-code-structure-for-testable/">จะทำระบบให้รองรับ Automated Test ได้อย่างไร (Testable)</a>&#8221; ไปคราวก่อน ได้รับผลตอบรับด้วยดี คราวนี้เลยมาเขียนเพิ่มเติมเพื่อเป็นตัวอย่างแก่ผู้ที่ต้องการนำไปพัฒนาระบบจริง โดยในบล็อกนี้ผู้อ่านจะได้เห็นภาพของสิ่งเหล่านี้ คือ</p>
<ul>
<li>กระบวนการคิดเพื่อเตรียมทำ Automated Test</li>
<li>Unit Test</li>
<li>Integration Test</li>
<li>DI (Dependency Injection)</li>
<li>Stub (Test Double)</li>
<li>Code Coverage</li>
</ul>
<p><span id="more-4455"></span></p>
<h2>ระบบที่จะทำในบล็อกนี้</h2>
<p>ผมต้องการทำระบบคิดเงินอัตราแลกเปลี่ยน โดยการดึงข้อมูลอัตราแลกเปลี่ยนกลางจาก <a href="https://iapi.bot.or.th/Developer?lang=th" target="_blank" rel="noopener">API ของธนาคารแห่งประเทศไทย</a> และบวกค่าธรรมเนียมการให้บริการไปด้วย (ทำตัวเป็นคนกลางรับแลกเปลี่ยนเงินตรา) โดยรายละเอียดของเงื่อนไขมีดังนี้</p>
<ul>
<li>สามารถแลกเงินได้ตั้งแต่ 1$ ขึ้นไป</li>
<li>ต้องแลกเป็นเงินจำนวนเต็ม 1$ เท่านั้น ไม่สามารถแลกเงินเป็นเศษได้</li>
<li>บวกค่าธรรมเนียมการแลกเงิน 2.50% จากราคาปกติ เมื่อลูกค้าแลกเงินในช่วง 1$ &#8211; 100$</li>
<li>บวกค่าธรรมเนียมการแลกเงิน 2.15% จากราคาปกติ เมื่อลูกค้าแลกเงินในช่วง 101$-500$</li>
<li>บวกค่าธรรมเนียมการแลกเงิน 2.00% จากราคาปกติ เมื่อลูกค้าแลกเงินตั้งแต่ 501$ ขึ้นไป</li>
<li>แสดงค่าอัตราแลกเปลี่ยนเป็นทศนิยม 2 ตำแหน่ง ถ้ามีเศษให้ปัดขึ้นเสมอ โดยพิจารณาที่ตำแหน่งที่ 2 (ตัวอย่าง 1 USD = 35.00THB + 2.5% = 35.875THB = 35.88THB)</li>
<li>ให้ดึงค่าอัตราแลกเปลี่ยนจาก BOT API ที่ <a href="https://iapi.bot.or.th/Developer?lang=th" rel="nofollow">https://iapi.bot.or.th/Developer?lang=th</a> (ใช้ API: อัตราแลกเปลี่ยนเฉลี่ย &#8211; รายวัน)</li>
</ul>
<p>ตัวอย่างของโจทย์นี้คือ &#8230; ผมเป็นบริษัทรับแลกเงิน จะคำนวณเงินบาท (THB) ที่ลูกค้าต้องจ่าย เพื่อแลกกับจำนวนเงินดอลลาร์ (USD) ที่ต้องการ เช่น ลูกค้าต้องการเงินจำนวน 1$ เมื่อดึงข้อมูลจากธนาคารแห่งประเทศไทยได้ 35 บาท ผมจึงบวกค่าธรรมเนียมอีก 2.5% และปัดเศษขึ้นกลมๆ แปลว่าลูกค้าต้องจ่ายให้ผม 35.88 บาท</p>
<p>ดังนั้นลูกค้าจะแลกเงินเท่าไร ก็จะบวกค่าธรรมเนียม และแสดงผลว่าลูกค้าต้องจ่ายเงินบาทเท่าไร ตามจำนวนเงินดอลลาร์ที่เขาต้องการ</p>
<h2>จะทำ Automated Test ต้องเริ่มต้นที่ออกแบบ</h2>
<p>จากโจทย์ ผมกำหนด Input, Output ง่ายๆ ให้ระบบผมก่อน ในที่นี่ Ouput คือ &#8220;เงินบาทที่ลูกค้าต้องจ่าย&#8221; ส่วน Input ผมใส่ &#8220;จำนวนเงินดอลลาร์ที่ลูกค้าต้องการ&#8221; และผมขอเพิ่ม &#8220;วันที่อ้างอิงของอัตราแลกเปลี่ยน&#8221; ที่ผมต้องการด้วย เพื่อกำหนดค่าของวันให้คงที่ ใช้ในกรณีผมต้องการทดสอบ (และเผื่ออนาคตถ้าผมอยากเปลี่ยน Business Condition ทางวันที่)</p>
<p><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-4459 aligncenter" src="https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Blackbox.png" alt="" width="641" height="165" srcset="https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Blackbox.png 641w, https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Blackbox-600x154.png 600w" sizes="(max-width: 641px) 100vw, 641px" /></p>
<p>จาก Process กล่องดำๆ มืดมัว (Black Box) ผมจะแจกแจงเพื่อให้เข้าใจกระบวนการทำงานของมันเสียก่อน (White Box) ซึ่งผมถนัดคิดออกมาเป็นข้อๆก่อน จึงขอร่างไว้ดังนี้</p>
<div>
<ol>
<li><strong>รับค่า:</strong> จำนวนเงินดอลลาร์, วันที่ของอัตราแลกเปลี่ยนเงิน</li>
<li>ตรวจสอบจำนวนเงินมีค่าเป็นตัวเลขจำนวนเต็มบวกหรือไม่</li>
<li>เรียกข้อมูลอัตรแลกเปลี่ยนเงินจาก API ของธนาคารแห่งประเทศไทย โดยอ้างอิงจากวันที่ต้องการ</li>
<li>นำจำนวนเงินจำนวนเงินดอลลาร์คูณกับอัตราแลกเปลี่ยน Selling (ใช้ Selling เพราะหมายถึงอัตราที่ธนาคารขายให้เรา)</li>
<li>นำจำนวนเงินจำนวนเงินดอลลาร์ไปหา % ของธรรมเนียมที่จะไปคิด</li>
<li>นำจำนวนเงินบาทที่แปลงจากจำนวนเงินดอลลาร์ มาบวกกับค่าธรรมเนียมที่คิดเป็นเงินบาท</li>
<li>นำจำนวนเงินบาทหลังรวมค่าธรรมเนียมแล้วปัดเศษให้เหลือสองหลัก โดยปัดเศษที่ตำแหน่งที่ 2</li>
<li><strong>คืนค่า:</strong> จำนวนเงินบาทหลังรวมค่าธรรมเนียมและปัดเศษเหลือสองตำแหน่ง</li>
</ol>
<p>ซึ่งถ้าลองพิจารณากระบวนการดีๆแล้ว เราจะเห็นได้ว่า ข้อไหนมีการคำนวณ มีเงื่อนไขบ้างก็ประเมินไว้ก่อนเลยว่าอาจต้องทำ Unit Test และข้อไหนมีการเชื่อมต่อกับระบบภายนอกก็ต้องทำ Integration Test</p>
<p>คราวนี้ผมลองแปลงให้เป็น Flowchart ดู จะได้ง่ายต่อการอ่าน</p>
<p><img decoding="async" class="size-full wp-image-4462 aligncenter" src="https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Whitebox-Test.png" alt="" width="748" height="1074" srcset="https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Whitebox-Test.png 748w, https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Whitebox-Test-600x861.png 600w, https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Whitebox-Test-713x1024.png 713w, https://myifew.com/wp-content/uploads/2018/04/APIExchange-Problem-Whitebox-Test-488x700.png 488w" sizes="(max-width: 748px) 100vw, 748px" /></p>
<h2>มาทำ Test Cases และ Test Data กัน</h2>
<p>ตอนนี้เราเห็นกระบวนการทำงานทั้งหมดของระบบเราแล้ว ได้เห็นเส้นทาง ทางแยกที่จะเกิดขึ้น ดังนั้น ก็พอจะสรุป Test Cases ได้แล้ว</p>
<p>แต่เดี๋ยวก่อน! ถ้าจะเขียน Test ให้มันทดสอบได้ แปลว่าผมคาดหวังสิ่งใดไว้ ผมจะต้องได้ค่านั้นออกมาเสมอใช่ไหม สิ่งต่างๆใน Test ของผมควรจะต้องคงที่ด้วย ดังนั้นผมจึงกำหนดค่าคงที่ให้กับวันที่เสียก่อน เพราะมันเป็นค่าเดียวที่จะทำให้อัตราแลกเปลี่ยนผิดเพี้ยนไปจากความคาดหวัง (Expect Data) ที่ผมจะเขียนไว้ใน Test</p>
<p>ดังนั้น ในที่นี้ผมจะกำหนดให้ใช้อัตราแลกเปลี่ยนของวันที่ 1 กุมภาพันธ์ 2561 เป็นตัวทดสอบ โดยมีอัตราแลกเปลี่ยนที่ 1$ เท่ากับ 31.5408000 THB</p>
<p>เราก็จะได้ Test Cases ดังนี้</p>
<h4>Test Cases</h4>
<ol>
<li>สามารถแลกเงินที่มากกว่าหรือเท่ากับ 1$ แต่ไม่เกิน 100$ ที่อ้างอิงกับอัตราแลกเปลี่ยนของวันที่ 1 กพ 2018 และบวกค่าธรรมเนียม 2.50% ได้ถูกต้อง</li>
<li>สามารถแลกเงินที่มากกว่าหรือเท่ากับ 101$ แต่ไม่เกิน 500$ ที่อ้างอิงกับอัตราแลกเปลี่ยนของวันที่ 1 กพ 2018 และบวกค่าธรรมเนียม 2.15% ได้ถูกต้อง</li>
<li>สามารถแลกเงินที่มากกว่าหรือเท่ากับ 501$ ขึ้นไป ที่อ้างอิงกับอัตราแลกเปลี่ยนของวันที่ 1 กพ 2018 และบวกค่าธรรมเนียม 2.00% ได้ถูกต้อง</li>
<li>ไม่สามารถแลกเงินที่น้อยกว่า 1$ ได้</li>
</ol>
<p>จาก 4 Test Cases ข้างต้น ผมใช้หลักการ BVA (Boundary value analysis) เป็นตัวสร้าง Test Data ให้ผม หมายความว่า ค่าใดที่ดูเป็นค่าที่จะเปลี่ยนแปลง Business Condition จะต้องเอาค่าก่อนหน้าและหลังมาใช้ด้วย เช่น 500$ ผมก็จะหยิบ 499$ และ 501$ มาทดสอบด้วย (เชื่อเถอะ ทดสอบแบบนี้ดีแล้ว เพราะโปรแกรมเมอร์ชอบมีปัญหากับเครื่องหมาย &lt;, &lt;=, &gt;, &gt;= ในการตรวจสอบค่า)</p>
<p style="text-align: center;"><img decoding="async" class="alignnone size-full wp-image-4463 aligncenter" src="https://myifew.com/wp-content/uploads/2018/04/Boundary-value-analysis.jpg" alt="" width="500" height="311" />Photo: http://toolsqa.com/software-testing/boundary-value-analysis/</p>
<p>และเราก็จะได้ Test Data ดังนี้</p>
<h4>Test Data</h4>
<table>
<thead>
<tr>
<th>Test Cases</th>
<th>Date</th>
<th>Change USD</th>
<th>Selling THB</th>
<th>Selling THB with Fee</th>
<th>After Celling</th>
</tr>
</thead>
<tbody>
<tr>
<td>#1</td>
<td>1 Feb 2018</td>
<td>1</td>
<td>31.5408000</td>
<td>32.33</td>
<td>32.33</td>
</tr>
<tr>
<td>#1</td>
<td>1 Feb 2018</td>
<td>100</td>
<td>3154.08000</td>
<td>3232.932000</td>
<td>3232.94</td>
</tr>
<tr>
<td>#2</td>
<td>1 Feb 2018</td>
<td>101</td>
<td>3185.620800</td>
<td>3254.111647</td>
<td>3254.12</td>
</tr>
<tr>
<td>#2</td>
<td>1 Feb 2018</td>
<td>499</td>
<td>15738.859200</td>
<td>16077.244673</td>
<td>16077.25</td>
</tr>
<tr>
<td>#2</td>
<td>1 Feb 2018</td>
<td>500</td>
<td>15770.400000</td>
<td>16109.463600</td>
<td>16109.47</td>
</tr>
<tr>
<td>#3</td>
<td>1 Feb 2018</td>
<td>501</td>
<td>15801.940800</td>
<td>16117.979616</td>
<td>16117.98</td>
</tr>
<tr>
<td>#3</td>
<td>1 Feb 2018</td>
<td>1000</td>
<td>31540.800000</td>
<td>32171.616000</td>
<td>32171.62</td>
</tr>
<tr>
<td>#4</td>
<td>1 Feb 2018</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>#4</td>
<td>1 Feb 2018</td>
<td>0.1</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
</tbody>
</table>
<p>ปล. สังเกตว่า ในตารางผมมี Test Cases ประกบด้วยนะเออ เอาไว้เป็นเช็คลิสเล็กๆว่า ทำครบทุกเคสหรือไม่</p>
<h2>ในส่วนของโค้ดนั้น</h2>
<p>ในการเขียนโปรแกรมของโจทย์นี้ ผมใช้หลักการที่เรียกว่า TDD (Test Driven Development) ในการทำ เนื่องจากผมรู้ Test Cases รู้ Data Test ผมจึงเริ่มเขียนตั้งแต่ Test ได้เลย ..</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4466 aligncenter" src="https://myifew.com/wp-content/uploads/2018/04/stride-nyc-test-driven-development-chart-700x400.jpg" alt="" width="700" height="400" srcset="https://myifew.com/wp-content/uploads/2018/04/stride-nyc-test-driven-development-chart-700x400.jpg 700w, https://myifew.com/wp-content/uploads/2018/04/stride-nyc-test-driven-development-chart-700x400-600x343.jpg 600w" sizes="auto, (max-width: 700px) 100vw, 700px" />Photo: http://haselt.com/coding-dojo-with-tdd/</p>
<p>หลักการของ TDD ตามรูป</p>
<ol>
<li>เริ่มจากเขียนโค้ด Unit Test ก่อนเนอะ โดยเราระบุค่า Input และค่าที่คาดหวังว่าระบบเราจะต้องส่งกลับออกมา (Output, Expect Data)</li>
<li>พอรัน Unit Test มันก็จะไม่ผ่าน (จะผ่านได้ไงล่ะ! มันยังไม่มี Production Code ที่ใช้ทำงาน) <span style="color: #ff0000;">&lt;== กระบวนการสีแดง</span></li>
<li>เขียน Production Code ที่ง่ายที่สุด เพื่อให้ Unit Test แรกมันผ่าน เช่น ใน Unit Test คาดหวังไว้ว่าต้องได้ค่าคืนมาเป็น  32.33 ผมก็จะสร้างโค้ดโง่ๆ คือ ส่งเลข 32.33 นี้ออกมาเลย <span style="color: #008000;">&lt;== กระบวนการสีเขียว</span></li>
<li>ในการโค้ดแรกๆอาจยังเรียบๆ ไม่มีอะไร Refactoring ก็ข้ามไปก่อน<span style="color: #0000ff;"> &lt;== กระบวนการ Refactor</span></li>
<li>แล้วก็วนกลับไปเขียน Unit Test ใหม่ ในข้อที่สอง ตามตาราง Test Data</li>
</ol>
<p>ซึ่งโค้ดในส่วนของ Unit Test ทั้งหมดของผม หลังจากทำครบทุกเคสและมีการ Refactor แล้ว จะหน้าตาประมาณนี้</p>
<p><script src="https://gist.github.com/ifew/eef26279d1c1a02ec69658457af0f699.js"></script></p>
<p>คราวนี้ระบบของผมมีอยู่ Method หนึ่งทำการเชื่อมต่อกับ API ธนาคารของประเทศไทยด้วย ดังนั้น จะทำอย่างไรล่ะเพื่อให้เป็น Unit Test ได้ เพราะขัด<a href="http://www.somkiat.cc/good-unit-test-with-first/" target="_blank" rel="noopener">หลักการของ FIRST</a> อยู่สองข้อคือ</p>
<ul>
<li><strong>Fast</strong>: ต้องทำงานได้เร็ว &lt;== การมี Connection ออกไปข้างนอกจะช้า</li>
<li><strong>Repeatable</strong>: ทำซ้ำได้ ไม่ขึ้นกับระบบอื่นๆ &lt;== มันไม่สามารถทำซ้ำได้ ถ้าระบบภายนอกเชื่อมต่อไม่ได้</li>
</ul>
<p>และวิธีการที่เราจะทำต่อไปคือ ต้องจำลองค่า JSON ที่ส่งออกมาจาก API ธนาคารแห่งประเทศไทย ให้ได้ตามนี้</p>
<p><script src="https://gist.github.com/ifew/ccef1e89a4519e6559fa8ec63632d011.js"></script></p>
<p>ซึ่งวิธีข้างต้นเรียกว่าการ Stub (เป็นหนึ่งในหลักการของ <a href="http://www.somkiat.cc/test-double-mock-stub-and-dummy/" target="_blank" rel="noopener">Test Double</a>) ซึ่งหมายถึง ผมต้องการจำลองการทำงานอะไรบางอย่างเพื่อให้ได้ค่าเดิมออกมาเสมอ</p>
<p>คราวนี้ โค้ดที่ต่อ API ธนาคารแห่งประเทศไทย มีการเรียกใช้ Library ชื่อ HttpClient ซึ่งเป็นการเรียกออกไปที่ Internet จริงๆ ดังนั้น ก็ยังผิดหลักการทำ Unit Test อยู่ดี แม้ว่าผมจะจำลองการคืนค่า JSON ได้แล้วก็ตาม</p>
<p>วิธีที่ผมเลือกทำคือ ผมใช้หลักการ <a href="http://www.somkiat.cc/let-start-with-dependency-injection/" target="_blank" rel="noopener">Dependency Injection</a> ซึ่งผมไม่ได้ให้ &#8220;Method ที่เรียก API ธนาคารแห่งประเทศไทย&#8221; เป็นตัวเรียกใช้ HttpClient แต่ผมเปลี่ยนเป็น ใครก็ตามที่เรียกใช้ &#8220;Method ที่เรียก API ธนาคารแห่งประเทศไทย&#8221; ต้องส่ง HttpClient ให้มันด้วย โดยลองดูตัวอย่างต่อไปนี้ครับ</p>
<p>นี่คือโค้ด BotService ที่ผมเขียนไว้เรียกใช้ API ของธนาคารแห่งประเทศไทย ที่ผมมีการส่ง HttpClient เข้ามาที่ Constructor</p>
<p><script src="https://gist.github.com/ifew/b1937af1eada2aba5bd6a562d7837a4a.js"></script></p>
<p>และนี่คือโค้ด Integration Test ของผมที่ไปเรียกใช้ BotService มันอีกที ดังนั้น ผมจะต้องส่ง HttpClient ให้มันด้วย</p>
<p><script src="https://gist.github.com/ifew/c0892111d586b361bd89093978b28f3c.js"></script></p>
<p>ซึ่งจากโค้ดดังกล่าว ใน BotService ผมไม่ใส่ HttpClient ให้กับ BotService เลย เพราะผมต้องการให้ผู้ที่เรียกใช้มันเป็นตัวบอกเองว่า จะส่ง &#8220;HttpClient จริงๆ&#8221; หรือ ส่ง &#8220;HttpClient ที่ Stub&#8221; ให้มัน โดยในตัวอย่างข้างต้น ผมเขียน Integration Test  ดังนั้น ผมจึงส่ง &#8220;HttpClient จริงๆ&#8221; ให้มันไปเลย</p>
<p>คราวนี้ ถ้ากลับมาดูของ Unit Test ผมต้องการส่ง&#8221;HttpClient ที่ Stub&#8221; ให้มันแทน ซึ่งในที่นี้ผมใช้ Library ชื่อ <a href="https://github.com/moq/moq4" target="_blank" rel="noopener">Moq</a> (ถ้าของ Java เช่น <a href="https://github.com/azagniotov/stubby4j" target="_blank" rel="noopener">Stubby4j</a>) ในการทำ Stub เพื่อบอกว่า</p>
<p>ถ้ามีการเรียก URL  ชื่อว่า <em>&#8220;https://iapi.bot.or.th/Stat/Stat-ExchangeRate/DAILY_AVG_EXG_RATE_V1/?start_period=2018-02-01&amp;end_period=2018-02-01&amp;currency=USD&#8221;</em> จะต้องส่งค่า JSON ที่ผมต้องการออกมาเสมอ ดังนั้นจะได้โค้ดของ Unit Test ชื่อ <span class="pl-en">ExchangeRateServiceTest</span>() ดังนี้</p>
<p><script src="https://gist.github.com/ifew/0b83f7194a17d96f10a1ee92cd931d25.js"></script></p>
<p>คราวนี้ เมื่อลองรันทดสอบทั้ง Unit Test และ Integration Test ทั้งหมด จะเกิด Unit Test 9 ตัว และ Integration Test 1 ตัว</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4468" src="https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.44.43.png" alt="" width="1046" height="512" srcset="https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.44.43.png 1046w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.44.43-600x294.png 600w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.44.43-1024x501.png 1024w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.44.43-768x376.png 768w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.44.43-700x343.png 700w" sizes="auto, (max-width: 1046px) 100vw, 1046px" /></p>
<p>และถ้าหากเรามาเช็คเรื่อง Code Coverage ว่า โค้ดที่เราเขียนมาทั้งหมดนี้ มีการ Test ครอบคลุมครบทุกส่วนของ Business Condition หรือไม่ ผมที่ได้คือ ..</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4469" src="https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.48.59.png" alt="" width="1150" height="486" srcset="https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.48.59.png 1150w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.48.59-600x254.png 600w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.48.59-1024x433.png 1024w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.48.59-768x325.png 768w, https://myifew.com/wp-content/uploads/2018/04/Screen-Shot-2561-04-10-at-15.48.59-700x296.png 700w" sizes="auto, (max-width: 1150px) 100vw, 1150px" /></p>
<h2>สรุป</h2>
<p>ด้วยกระบวนการทั้งหมดที่ผมเขียนไว้ ตั้งแต่การออกแบบ จนมาทำเป็น Unit Test และ Integration Test ซึ่งถ้าทำได้ มันคือการทำให้เกิด Automated Test ดังนั้นไม่ว่าโค้ดเราจะแก้ไขอะไรไป เราก็แค่กลับมารัน Unit Test และ Integration Test ของเรา ให้มันตรวขสอบให้เราทุกเคสแบบอัตโนมัติว่ายังทำงานได้ถูกต้องหรือไม่ จากนั้นเราก็มาตรวจสอบอีกชั้นว่า นอกจากถูกต้องแล้ว ชุด Test ของเรา ยังครอบคลุมทุกบรรทัดที่เราเขียนไว้หรือไม่</p>
<p>เมื่อได้ Automated Test แล้ว การจะไปทำกระบวนการ Continous Integration, Continous Deployment ก็ไม่ยากแล้วครับ เพราะเรามั่นใจได้ว่าโค้ดของเรามีการทดสอบและครอบคลุมเสมอ พร้อม Deploy ไปสู่ระบบต่างๆ ได้ทันทีเมื่อเขียนระบบเสร็จ เกิดการ feedback จากผู้ใช้งานได้ไว และแก้ไขงานได้ไว มันก็จะเป็นส่วนหนึ่งที่ไปเสริมความให้เกิด Agile ในองค์กรต่อไป</p>
<p>โค้ดตัวอย่างทั้งหมด ดูได้ที่ <a href="https://github.com/ifew/dojo-BotExchangeRate" target="_blank" rel="noopener">https://github.com/ifew/dojo-BotExchangeRate</a></p>
<hr />
<p><strong>เพิ่มเติมทิ้งท้าย</strong></p>
<ul>
<li>ผมสังเกตว่า API Exchange Rate ของธนาคารแห่งประเทศไทย ถ้าวันไหนไม่มีอัตราแลกเปลี่ยน เช่น วันหยุด, วันเสาร์-อาทิตย์ ช่วงเวลา 00:00-17:59 จะใช้ค่าเงินล่าสุดของวันก่อนหน้านั้น แต่ถ้าวันปัจจุบันมีอัตราแลกเปลี่ยนและเวลามากกว่าหรือเท่ากับ 18:00 จะใช้ค่าเงินของวันนั้น</li>
<li>ในตัวอย่าง ผมไม่ได้ตรวจสอบว่า ถ้า API Exchange Rate ของธนาคารแห่งประเทศไทย ไม่สามารถเรียกใช้งานได้ จะต้องทำอย่างไรต่อไป</li>
<li>ทำไมต้องปัดเศษทศนิยมขึ้นที่ตำแหน่งที่สองด้วย? .. เพราะผมหน้าเลือดครับ ฮี่ๆ</li>
</ul>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/4455/design-and-develop-code-for-testable/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>จะทำระบบให้รองรับ Automated Test ได้อย่างไร (Testable)</title>
		<link>https://myifew.com/4414/design-project-code-structure-for-testable/</link>
					<comments>https://myifew.com/4414/design-project-code-structure-for-testable/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Sat, 17 Feb 2018 19:07:10 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Automation Testing]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Integration Test]]></category>
		<category><![CDATA[Test Strategy]]></category>
		<category><![CDATA[Testable]]></category>
		<category><![CDATA[Unit Test]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=4414</guid>

					<description><![CDATA[ตอนที่หัดเขียน Automated  Test ผมเคยเจอปัญหาว่า โค้ดเก่าที่เขียนมา มันไม่รองรับการเขียน Unit Test, Integration Test เอาซะเลย แกะยากเหมือนกัน จนแล้วจนรอดก็ลองทำใหม่ แล้วฝึกกับโจทย์ไปเรื่อยๆ จนพอตกผลึกได้บ้าง ว่าถ้าจะทำระบบให้รองรับการ Test (Testable) จะต้องทำอย่างไร

วันก่อน มีน้องในทีมถามว่า "ผมจะเขียน Integration Test อย่างไรดี" ก็เลยได้ย้อนไปทบทวนประสบการณ์ หาข้อมูล และถามจากผู้รู้เพิ่มเติม จึงกลับไปเล่าให้น้องฟัง ก็คิดว่ามีประโยชน์ จึงอยากแชร์ไว้มาแลกเปลี่ยนความรู้กัน]]></description>
										<content:encoded><![CDATA[<p>ตอนที่หัดเขียน Automated  Test ผมเคยเจอปัญหาว่า โค้ดเก่าที่เขียนมา มันไม่รองรับการเขียน Unit Test, Integration Test เอาซะเลย แกะยากเหมือนกัน จนแล้วจนรอดก็ลองทำใหม่ แล้วฝึกกับโจทย์ไปเรื่อยๆ จนพอตกผลึกได้บ้าง ว่าถ้าจะทำระบบให้รองรับการ Test (Testable) จะต้องทำอย่างไร</p>
<p>วันก่อน มีน้องในทีมถามว่า &#8220;ผมจะเขียน Integration Test อย่างไรดี&#8221; ก็เลยได้ย้อนไปทบทวนประสบการณ์ หาข้อมูล และถามจากผู้รู้เพิ่มเติม จึงกลับไปเล่าให้น้องฟัง ก็คิดว่ามีประโยชน์ จึงอยากแชร์ไว้มาแลกเปลี่ยนความรู้กัน<span id="more-4414"></span></p>
<h2>ต้องเข้าใจโครงสร้างโปรเจ็คเสียก่อน</h2>
<p>ทุกวันนี้เราเข้าใจโครงสร้างของโปรเจ็คเราดีแล้วหรือยัง หรือถ้าเข้าใจดีแล้ว เราได้แยกส่วนชัดเจนไหมว่าส่วนไหนเป็นโค้ดของระบบ (Production Code) และส่วนไหนเป็นโค้ดของการทดสอบ (Test Code)</p>
<p>ขอยกตัวอย่าง ถ้าผมจะเขียนโปรเจ็คตัวหนึ่งคือ ระบบ API (My API) โดยมีการเชื่อมต่อไปยังระบบ API ข้างนอก (External API) จากนั้นผมมีโปรเจ็ค Test แยกออกมาอีกตัวหนึ่ง รูปร่างหน้าตามันก็จะเป็นเช่นนี้</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="aligncenter wp-image-4424 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest1.png" alt="" width="591" height="277" />(รูปโครงสร้างโปรเจ็ค)</p>
<p>ลองเจาะลึกเข้าไปในการทำงานของโปรเจ็ค ในที่นี้ผมเขียนเป็น MVC ซึ่งถ้าผมเป็น User ผมจะเรียกใช้ API ตัวหนึ่ง ผ่าน Controller จากนั้นไปเรียก Services เพื่อทำการติดต่อไปยัง External API และรับค่ามาแสดงให้ User ได้เห็น</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="aligncenter wp-image-4425 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest2.png" alt="" width="654" height="277" srcset="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest2.png 654w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest2-600x254.png 600w" sizes="auto, (max-width: 654px) 100vw, 654px" />(รูปการทดงานภายในโปรเจ็ค)</p>
<h2>แยกโปรเจ็คของชุดทดสอบ (Test) ให้ชัดเจน</h2>
<p>จากรูปการทดงานภายในโปรเจ็ค, ผมสมมติว่า Controller ผมมี Business Logic อะไร เป็นเพียงการรับ Request และส่งผ่าน Data ไปให้ Services ทำงานต่อ คราวนี้ถ้าผมจะเขียนทดสอบ ผมควรต้องเขียนทดสอบที่ Services ซึ่งในที่นี้ผมจะทดสอบการทำงานในชั้นที่เล็กที่สุด นั่นคือ Unit Test</p>
<p>แต่ตามทฤษฎีแล้ว คุณสมบัติที่ดีของ Unit Test จะต้องเป็นไปตาม<a href="http://www.somkiat.cc/good-unit-test-with-first/" target="_blank" rel="noopener">หลักการของ FIRST</a> คือ</p>
<ul>
<li>Fast: ทำงานได้เร็ว</li>
<li>Isolated: เป็นอิสระจากกัน</li>
<li>Repeatable: ทำซ้ำได้ ไม่ขึ้นกับระบบอื่นๆ เช่น API ภายนอก, Database, File System</li>
<li>Self-Verify: แสดงให้เห็นผลการทดสอบ ผ่าน หรือไม่ผ่านได้อย่างชัดเจน</li>
<li>Timely: เขียนให้ถูกเวลา คือ ควรมี Test ก่อน เพื่อให้ได้เข้าใจปัญหา และจึงเขียนโค้ดเข้ามาแก้ปัญหานั้น</li>
</ul>
<p>หมายความว่า ถ้าผมทำ Unit Test ผมจะไม่สามารถทำการทดสอบได้เลย ถ้า External API พัง หรือ Internet ล่ม เป็นต้น</p>
<p>ดังนั้น ผมต้องเขียนโค้ดทดสอบเพิ่มเข้ามาอีกชุด  ที่เรียกว่า Integration Test</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="aligncenter wp-image-4417 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-IntegrationTest.png" alt="" width="591" height="277" />(รูปการทำงานภายในโปรเจ็คที่มี Unit Test และ Integration Test)</p>
<p>ถ้าดูจากเส้นการทำงาน ผมจะเขียน Unit Test เพื่อทดสอบแค่กระบวนการภายใน Services ของผมเอง (เส้นสีน้ำเงิน) ส่วน Integration Test ผมจะทดสอบกระบวนการที่ Services ผมไปเรียกใช้ External API (เส้นสีเขียว)</p>
<h2>วิเคราะห์การทำงานของโค้ด และแยกแยะให้ถูก</h2>
<p>มาถึงตรงนี้พอเห็นภาพโครงสร้างกว้างๆ แล้วใช่ไหมครับว่า เราจะแยกการทดสอบอย่างไร คราวนี้เราจะลองลงไปให้ลึกเข้าไปอีก ในระดับของโค้ด</p>
<p>ผมสมมติว่า Services ที่ผมทำขึ้นมี Method ชื่อว่า IsAdd() โดยรับหมายเลขสองตัวมาบวกกัน จากนั้นส่งไปให้ External API ตรวจสอบว่าคำนวณถูกต้องไหม ถ้าถูก ให้คืนค่า True กลับมา</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="aligncenter wp-image-4422 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method.png" alt="" width="704" height="387" srcset="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method.png 704w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-600x330.png 600w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-700x385.png 700w" sizes="auto, (max-width: 704px) 100vw, 704px" />(รูปของ Method IsAdd() ที่ยังไม่รู้กระบวนการภายใน)</p>
<p>จากคำอธิบายที่ผ่านมา มันยังเป็นเพียง Black Box ที่เราแทบไม่เห็นการทำงานของมันเลย รู้แค่ว่าส่งอะไรเข้าไป และคืนค่าอะไรกลับมา ดังนั้น เราจะต้องวิเคราะห์การทำงานของโค้ดเราให้กระจ่างเสียก่อน</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="aligncenter wp-image-4418 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Explain.png" alt="" width="704" height="387" srcset="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Explain.png 704w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Explain-600x330.png 600w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Explain-700x385.png 700w" sizes="auto, (max-width: 704px) 100vw, 704px" />(รูปของ Method IsAdd() ที่เราวิเคราะห์ว่ามีการทำงานใดเกิดขึ้นบ้าง)</p>
<p>หลังจากวิเคราะห์ออกมา เราพบว่ามีการทำงานสองส่วนคือ</p>
<ul>
<li>Process A: เป็นการคำนวณเลขสองตัวที่รับเข้ามาว่าบวกกันได้เท่าไร</li>
<li>Process B: นำค่าที่ได้จากการคำนวณ Process A ส่งไปให้ External API และรับค่าผลลัพธ์กลับมา</li>
</ul>
<p>แปลว่า เราต้องแบ่ง Method ตัวนี้ออกเป็นสอง Method เพื่อทำการทดสอบ Unit Test กับ Process A และ Integration Test กับ Process B</p>
<p>ถ้าค้นพบว่างานที่เราทำอยู่ กำลังเป็นเช่นนี้ และต้องแยกมันออกมา นั่นคือ เราเขียน Method นี้ ไม่ดีแต่แรกแล้วครับ เพราะอย่าลืมว่า 1 Method ควรจะมีเพียง 1 การทำงานเท่านั้น</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="aligncenter wp-image-4421 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full.png" alt="" width="757" height="387" srcset="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full.png 757w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-600x307.png 600w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-700x358.png 700w" sizes="auto, (max-width: 757px) 100vw, 757px" />(รูปของ Method IsAdd() ที่เราวิเคราะห์การทำงาน และแบ่งชุดทดสอบ)</p>
<h2>ต้องรองรับกระบวนการ Continuous Integration ได้ด้วย</h2>
<p>เมื่อโครงสร้างของโปรเจ็คและโครงสร้างของโค้ดเรา สามารถรองรับการทดสอบได้แล้ว สิ่งที่ต้องคิดต่อมาคือ จะนำเข้ากระบวนการ Continuous Integration ได้อย่างไร เพื่อให้เกิด Pipeline และ Feedback Loop ที่เร็ว</p>
<p>เพราะ Unit Test สามารถอยู่ได้ด้วยตัวเอง แต่ Integration Test จะต้องเกี่ยวข้องกับระบบอื่นๆ เช่น  Database หรือ File System ซึ่งระบบเหล่านี้ควรมีประบวนการพร้อมใช้งานก่อน ดังนั้น ถ้ามันอยู่ร่วมโปรเจ็คเดียวกัน คงไม่ดีแน่นอน และไม่รู้ได้ด้วยว่าถ้าเกิดการรันและ Fail มันเกิดที่กระบวนการของ Unit หรือ Integration กันแน่ จึงควรแยกโปรเจ็คของชุดทดสอบนี้ออกจากกัน</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="aligncenter wp-image-4420 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated.png" alt="" width="757" height="387" srcset="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated.png 757w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated-600x307.png 600w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated-700x358.png 700w" sizes="auto, (max-width: 757px) 100vw, 757px" />(รูปของ Method IsAdd() ที่เราแบ่งชุดทดสอบ ออกเป็น 2 Project)</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="alignnone wp-image-4426 size-full" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Build_Pipeline.png" alt="" width="782" height="63" srcset="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Build_Pipeline.png 782w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Build_Pipeline-600x48.png 600w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Build_Pipeline-768x62.png 768w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Build_Pipeline-700x56.png 700w" sizes="auto, (max-width: 782px) 100vw, 782px" /><br />
(รูปตัวอย่างเมื่อแยกโปรเจ็ค Unit Test, Integration Test แล้ว สามารถกำหนด Pipeline ให้เหมาะสมได้)</p>
<h2>Tip: ใช้ Postman ทดสอบ Integration Test จะได้หรือไม่</h2>
<p>คำถามนี้ตอนแรกต้องยอมรับว่า ผมก็สับสนเหมือนกัน แต่เพิ่งได้ข้อสังเกตมาจากผู้รู้ว่า ความต่างระหว่างที่เราเขียนโค้ดทดสอบเพื่อเรียก API ตัวเอง กับใช้ Postman เพื่อทดสอบ API ตนเอง จะเรียกทั้งคู่เป็น Integration Test ได้หรือไม่</p>
<p>คำตอบคือ ใช้ Postman ทดสอบเรียก API จะไม่เรียกเป็น Integration Test แม้ว่าภายใน API นั้นจะเรียกไปที่ต่างๆจริง คืนค่าจริงกลับมา, แต่ถ้าให้ถูกต้องจะเรียกว่า End-to-End Testing</p>
<p>เพราะเราเอาระบบภายนอก (Postman) เรียกไปที่ API ว่าทำงานได้หรือไม่ แต่เราไม่ได้ทดสอบว่าระบบทั้งสองทำงานถึงกันได้หรือไม่ (เส้นสีชมพู) นั่นจึงเป็นที่มาว่า ใช้ Postman ก็อาจไม่ชัวร์ว่า ระบบเราจะทำงานได้จริง</p>
<p>หรือให้ลองนึกอีกที เคยไหมครับ ที่ใช้ Postman ทดสอบ API ได้ แต่พอรันโค้ด กลับทำงานไม่ได้ เพราะมี คอมพิวเตอร์เรามี Environment เรียกใช้ต่างกันกับเครื่อง Server เป็นต้น</p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-4419 aligncenter" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated-Tools.png" alt="" width="769" height="419" srcset="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated-Tools.png 769w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated-Tools-600x327.png 600w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated-Tools-768x418.png 768w, https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-Method-Full-Separated-Tools-700x381.png 700w" sizes="auto, (max-width: 769px) 100vw, 769px" /></p>
<h2>Tip: ถ้าอยากทดสอบทุกกระบวนการ แต่ไม่ต่อไปถึง External API จะทำอย่างไร</h2>
<p>เป็นคำถามที่ถูกถามบ่อยเหมือนกัน และโปรแกรมเมอร์ก็มักเจอบ่อยเช่นกันว่า</p>
<ul>
<li>ถ้าจะทดสอบ Services ตัวเอง โดยยังไม่อยากไปเชื่อมต่อระบบภายนอก</li>
<li>อยากทดสอบ Services ตัวเอง แต่ระบบภายนอกยังทำไม่เสร็จ</li>
<li>อยากทดสอบ Services ตัวเอง แต่ระบบภายนอกทำงานช้า ไม่ทันใจ</li>
</ul>
<p>จากปัญหาข้างต้น เราสามารถนำวิธีการชื่อว่า Stub จาก <a href="http://www.somkiat.cc/test-double-mock-stub-and-dummy/" target="_blank" rel="noopener">Test Double</a> มาใช้ได้ คือทำ Stub ตัว External API มันซะเลย เพื่อให้ได้ค่าที่เราต้องการเสมอ และไม่ต้องออกไปรับค่าจริงจาก External API</p>
<p>ถามว่าแล้วจะทำไปทำไม ก็ต้องตอบว่า เมื่อเราทดสอบการทำงานย่อยๆแล้ว (Unit Test) และทดสอบการทำงานที่สองระบบเชื่อมถึงกันแล้ว (Integration Test) เราอยากจะสร้างความมั่นใจเพิ่มไหมล่ะ ว่า ถ้าทั้งสองการทดสอบทำงานร่วมกัน มันยังถูกต้องอยู่</p>
<p>เราจะเรียกการทดสอบแบบนี้ว่า Component Test ซึ่งในที่นี้เราต้องการทดสอบการทำงานทั้งหมดของ Service เรา แต่เราไม่ได้สนใจว่า External API จะอยู่หรือจะตาย ทำงานได้ถูกต้องหรือไม่ ดังนั้น เราจึงต้องจำลอง External API มันขึ้นมา โดยใช้ Stub นั่นเอง (เขียนเองก็ได้ หรือหา Library ฟรีมาใช้ก็ได้ เช่น  .Net Core: <a href="https://github.com/markvincze/Stubbery" target="_blank" rel="noopener">Stuberry</a>, Java: <a href="https://github.com/azagniotov/stubby4j" target="_blank" rel="noopener">Stubby4j</a>)</p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-4416 aligncenter" src="https://myifew.com/wp-content/uploads/2018/02/IntegrationTest-ComponentTest.png" alt="" width="591" height="277" /></p>
<h2>สรุป</h2>
<p>การทำระบบให้รองรับ Automated Test เป็นอะไรที่สนุกเหมือนกัน ต้องวางแผนตั้งแต่แรกเลยว่าจะเป็นอย่างไร แล้วส่วนมากเราก็มักไม่ทำกันซะด้วย เพิ่งมาตามเก็บเอาตอนหลัง เหมือนที่ผมเกริ่นไปตอนต้น ว่าถ้ามีโปรเจ็คเก่าแล้วเอามาทำ เป็นอะไรที่ต้องแก้เยอะพอสมควร แต่ถ้าเป็นโปรเจ็คใหม่ ก็สามารถออกแบบให้ดีตั้งแต่ต้นได้เลย หากใครผ่านมาอ่าน มีคำแนะนำเพิ่มเติม หรือคำถาม สามารถแลกเปลี่ยนความรู้กันได้เลยนะครับ</p>
<p>สำหรับตัวอย่างการแยก Test ลองดูที่ GitHub ของผมได้ครับ มีแต่ <a href="https://github.com/ifew/netcore-lab" target="_blank" rel="noopener">.NET Core</a> นะ กำลังอิน ฮ่าๆ (<a href="https://github.com/ifew/netcore-lab/blob/master/tests/api.UnitTest/BotTest.cs">BOT UnitTest</a>, <a href="https://github.com/ifew/netcore-lab/blob/master/tests/api.IntegrationTest/BotTest.cs" target="_blank" rel="noopener">BOT IntegrationTest</a>, <a href="https://github.com/ifew/netcore-lab/blob/master/src/api/Services/BotService.cs" target="_blank" rel="noopener">BOT Service</a>)<br />
<script>
ga('set', 'page', '/testable');
ga('send', 'pageview');
</script></p>
<hr />
<p>อ่านภาคการนำไปประยุกต์ใช้ต่อได้ที่ <a href="https://myifew.com/4455/design-and-develop-code-for-testable/">ภาคปฎิบัติ: จะทำระบบให้รองรับ Automated Test ได้อย่างไร (Testable)</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/4414/design-project-code-structure-for-testable/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>เริ่มต้นทำ CI/CD – Automation Deployment ด้วย Git และ Jenkins (3 &#8211; จบ)</title>
		<link>https://myifew.com/4072/automation-deploy-git-bitbucket-jenkins/</link>
					<comments>https://myifew.com/4072/automation-deploy-git-bitbucket-jenkins/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Sun, 18 Jun 2017 06:43:31 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Automation Testing]]></category>
		<category><![CDATA[Automation Tools]]></category>
		<category><![CDATA[Bitbucket]]></category>
		<category><![CDATA[Continuous Delivery]]></category>
		<category><![CDATA[Continuous Deployment]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Jenkins]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=4072</guid>

					<description><![CDATA[เมื่อเขียนโค้ดเสร็จและทำการทดสอบเสร็จ จะดีกว่าไหม ถ้าโค้ดเราจะขึ้น Production Server ได้อัตโนมัติทันที]]></description>
										<content:encoded><![CDATA[<p>ความเดิมสองตอนก่อนคือ <a href="https://myifew.com/4027/how-to-setup-jenkins-automation-tools/">วิธีติดตั้ง Jenkin</a> และ <a href="https://myifew.com/4039/automation-testing-by-phpunit-and-jenkins/">วิธีใช้ PHPUnit เพื่อทำ Automation Test</a> ซึ่งเราก็ได้เห็นภาพของ CI (Continuous Integration) กันมาแล้ว มาบล็อกครั้งนี้เป็นตอนสุดท้ายของซีรี่ &#8220;เริ่มต้นทำ CI/CD&#8221; ก็คือ หลังจากเขียนโค้ดและทดสอบผ่านทั้งหมด เราจะให้ Jenkins นำโค้ดที่ทำเข้าสู่ Production Server ให้อัตโนมัติ (Automation Deployment)<span id="more-4072"></span></p>
<h2>จัดการสิทธิเพื่อใช้ Deployment</h2>
<p>ขั้นตอนนี้จะให้นำ SSH Key ของ Jenkins ไปทำความรู้จักกับ Bitbucket<br />
ก่อนอื่น ทำการตรวจสอบก่อนเลยว่า user jenkins ใน server เรา เชื่อมต่อ bitbucke.org ได้ไหม ด้วยคำสั่ง</p>
<p>[wc_code]$ sudo -u jenkins ssh -vT git@bitbucket.com[/wc_code]</p>
<p>ให้ดูบรรทัดท้ายๆ ถ้ามันมี username ที่เราใช้บน Bitbucket แปลว่ามันรู้จักกันแล้ว</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4073" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-13_01_26-root@myifew_-_var_www_test_ci_cd.png" alt="" width="589" height="154" /></p>
<p>แต่ถ้าขึ้นว่า Permission Denied  ให้ทำการ copy public key ไปไว้บน bitbucket ครับ โดยวิธีการดู Jenkins public key คือ</p>
<p>[wc_code]$ sudo vi /var/lib/jenkins/.ssh/id_rsa.pub[/wc_code]</p>
<p>เป็นอันเสร็จเรียบร้อย</p>
<h2>สร้างโฟลเดอร์เว็บไซต์ และไฟล์คำสั่ง Deployment</h2>
<p>ไฟล์เว็บไซต์ผมจะเก็บไว้ที่ /var/www/test_ci_cd และเมื่อมีโฟลเดอร์แล้ว ให้ clone project ลงมาทั้งหมด</p>
<p>[wc_code]$ sudo mkdir /var/www/test_ci_cd[/wc_code]</p>
<p>[wc_code]$ sudo git clone git@bitbucket.org:few/test-project.git .[/wc_code]</p>
<p>ทำการสร้างไฟล์เพื่อใช้ Deployment ซึ่งในที่นี้ผมใช้ชื่อ deploy.php</p>
<p>[wc_code]$ sudo vi /var/www/test_ci_cd/deploy.php[/wc_code]</p>
<p>ในไฟล์มีโค้ดประมาณนี้</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4111" src="https://myifew.com/wp-content/uploads/2017/06/2017-06-24-22_49_57-root@myifew_-_var_www_test_ci_cd.png" alt="" width="394" height="119" /></p>
<p>ซึ่งเป็นโค้ดที่ Jenkins จะเอาไว้ใช้งาน โดยมีขั้นตอนคือ</p>
<ol>
<li>เข้าโฟลเดอร์ที่เก็บเว็บไซต์ (หรือเก็บไฟล์ deploy.php)</li>
<li>ทำการล้างไฟล์เก่าออกทั้งหมด (optional)</li>
<li>ดึงไฟล์ล่าสุดที่มีการเปลียนแปลงและผ่านการทำสอบแล้ว ลงมาบน server</li>
</ol>
<p>จากนั้นทำการบันทึก ก็เสร็จแล้ว</p>
<h2>เพิ่ม Config Deployment บน Jenkins</h2>
<p>เข้าไปที่ Project ที่เราใช้ทำงาน จากนั้นเพิ่มในส่วนของ Build จุดเดียวกับที่ตั้งให้ทำ PHPUnit ดังนี้</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4075" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-13_19_20-Test_CI_CD_Bickbucket-Config-Jenkins.png" alt="" width="862" height="358" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-13_19_20-Test_CI_CD_Bickbucket-Config-Jenkins.png 862w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-13_19_20-Test_CI_CD_Bickbucket-Config-Jenkins-600x249.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-13_19_20-Test_CI_CD_Bickbucket-Config-Jenkins-768x319.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-13_19_20-Test_CI_CD_Bickbucket-Config-Jenkins-700x291.png 700w" sizes="auto, (max-width: 862px) 100vw, 862px" /></p>
<p>ซึ่ง folder path และชื่อไฟล์ ให้เปลี่ยนไปตามที่เราตั้งค่าไว้ด้วยนะ</p>
<h2>ลองทดสอบ Automation Deployment</h2>
<p>ตอนนี้ไฟล์ที่ผมใช้ทดสอบทำงานบวกเลขได้ตามปกติ และไฟล์ test unit ผมก็ยังใช้งานได้ตามปกติ<br />
ผมจะลองเพิ่มข้อความบนไฟล์ calculate.php โดยยังไม่ได้แก้ไขอะไร เพื่อดูว่าระบบไหลไปได้ถูกต้องหรือไม่</p>
<p>[wc_code]echo &#8216;Hi, automate test and deploy (success, all test case)&#8217;;[/wc_code]</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4077" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_46_59-D__www_test_test_ci_cd_src_calculate.php-Notepad.png" alt="" width="463" height="174" /></p>
<p>จากนั้นให้ Push เข้าสู่ระบบ</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4078" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-11_25_25-MINGW64__d_www_test_test_ci_cd.png" alt="" width="509" height="311" /></p>
<p>ลองเปิดดูบน Jenkins จะต้องพบว่ามัน Success</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4080" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_47_28-Test_CI_CD_Bickbucket-7560-Console-Jenkins-1024x476.png" alt="" width="1024" height="476" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_47_28-Test_CI_CD_Bickbucket-7560-Console-Jenkins-1024x476.png 1024w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_47_28-Test_CI_CD_Bickbucket-7560-Console-Jenkins-600x279.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_47_28-Test_CI_CD_Bickbucket-7560-Console-Jenkins-768x357.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_47_28-Test_CI_CD_Bickbucket-7560-Console-Jenkins-1200x557.png 1200w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_47_28-Test_CI_CD_Bickbucket-7560-Console-Jenkins-700x325.png 700w, https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_47_28-Test_CI_CD_Bickbucket-7560-Console-Jenkins.png 1492w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>จากนั้นลองเปิดบนเว็บไซต์ Production ที่เราได้เตรียมไว้ จะต้องแสดงข้อความที่เพิ่มขึ้นมา</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4081" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_50_17-myifew.com_test_ci_cd_src_calculate.php_.png" alt="" width="399" height="99" /></p>
<p>แปลว่า ระบบ Jenkins ทำการทำการ Automate Test และเมื่อทดสอบ Unit Test ผ่านทั้งหมด ก็ได้ Automate Deploy ให้เราเรียบร้อยแล้ววว</p>
<p>คราวนี้ มาลองทดสอบกรณีที่ Unit Test ทำงานไม่ผ่านบ้าง<br />
โดยผมได้แก้สูตรคิดเลข จาก บวก เป็น หาร ดังนั้น Unit Test ที่ผมทำการทดสอบไว้ทั้งหมดจะผิดทันที เพราะคำตอบมันเป็นผลลัพธ์ของการบวกเท่านั้น<br />
และผมได้ทำการแก้ไขข้อความด้วยว่า จะเกิดการ fail, all test case</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4082" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_48_04-D__www_test_test_ci_cd_src_calculate.php-Notepad-1.png" alt="" width="484" height="177" /></p>
<p>เช่นเดิม แก้ไขเสร็จ ทำการ git push ขึ้นไปบน Bitbucket</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4083" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_49_04-μTorrent-3.5-build-43804-32-bit.png" alt="" width="471" height="250" /></p>
<p>จากนั้นเมื่อไปดูบน Jenkins มันจะแสดงข้อความ FAILURES ขึ้นมา เพราะ Unit Test เราไม่ผ่าน</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4084" src="https://myifew.com/wp-content/uploads/2017/06/Test_CI_CD_Bickbucket-7561-Console-Jenkins--811x1024.png" alt="" width="811" height="1024" srcset="https://myifew.com/wp-content/uploads/2017/06/Test_CI_CD_Bickbucket-7561-Console-Jenkins--811x1024.png 811w, https://myifew.com/wp-content/uploads/2017/06/Test_CI_CD_Bickbucket-7561-Console-Jenkins--600x757.png 600w, https://myifew.com/wp-content/uploads/2017/06/Test_CI_CD_Bickbucket-7561-Console-Jenkins--768x969.png 768w, https://myifew.com/wp-content/uploads/2017/06/Test_CI_CD_Bickbucket-7561-Console-Jenkins--555x700.png 555w, https://myifew.com/wp-content/uploads/2017/06/Test_CI_CD_Bickbucket-7561-Console-Jenkins-.png 943w" sizes="auto, (max-width: 811px) 100vw, 811px" /></p>
<p>ดังนั้น ไฟล์บน Procution ก็จะไม่มีอะไรเปลี่ยนแปลง เพราะเมื่อ Unit Test ไม่ผ่าน มันจะไม่ไปเรียกไฟล์ deployment ทำต่อ</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4081" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-18-12_50_17-myifew.com_test_ci_cd_src_calculate.php_.png" alt="" width="399" height="99" /></p>
<h2>สรุป</h2>
<p>จากที่ได้ลองผิดลองถูกทำมาจนครบ ทำให้เห็นภาพได้ชัดเจนเลยครับว่า Continuous Integration และ Continuous Deployment มันสะดวกและเร็วขึ้นมาก ช่วยส่งเสริมกับเรื่องการส่งมอบงานเป็นรอบและส่งให้ได้ไว เพื่อให้มีการ feedback เร็ว ดังนั้น การเสียเวลาศึกษาและตั้งระบบขึ้นมาอีกสักหน่อย มันจะช่วยงานเราได้ในระยะยาว</p>
<p>แต่อย่างที่ชื่อเรื่องบอกนะครับ นี่เป็นเพียงการเริ่มต้น ดังนั้นขั้นตอนที่เราจะใช้งานจริงๆ อาจจะมีมากกว่านี้ที่ต้องเพิ่มขึ้น ทั้งการเพิ่ม Security, การเพิ่มเรื่องของ Test, การ git push ไปพักไว้ที่ dev branch ก่อน, การ Deploy ไปยัง Stating Server เพื่อทำ Acceptant Test แล้วค่อย merge git เข้าสู่ master branch และ deploy Production &#8230; ผมก็คงเขียนไม่หมด เอาเป็นว่า ไปเผชิญโลกกว้างกันดูครับ</p>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4089 aligncenter" src="https://myifew.com/wp-content/uploads/2017/06/BestPractices_for_a_MatureContinuousDeliveryPipeline.png" alt="" width="595" height="451" />(รูปจาก http://www.bogotobogo.com/DevOps/DevOps_CI_CD_Pipeline_Sample.php)</p>
<p>ดังนั้น วิธีการของผมก็ไม่ใช่สิ่งที่ดีที่สุด ถ้าหากผิดพลาดประการใด หรือมีคำแนะนำเพิ่มเติม มาแลกเปลี่ยนกันได้ครับ และกราบขอคำชี้แนะด้วยคร้าบบ..</p>
<p>เว็บอ้างอิง</p>
<ul>
<li><a href="http://chrisbeaver.com/2014/10/automatically-deploy-private-git-repo-server/" target="_blank" rel="noopener">Automatically Deploy Private Git Repo to Server</a></li>
<li>รูปปกจาก <a href="http://www.mindtheproduct.com/2016/02/what-the-hell-are-ci-cd-and-devops-a-cheatsheet-for-the-rest-of-us/" target="_blank" rel="noopener">http://www.mindtheproduct.com/2016/02/what-the-hell-are-ci-cd-and-devops-a-cheatsheet-for-the-rest-of-us/</a></li>
</ul>
<p>ซี่รี่ &#8220;เริ่มต้นทำ CI/CD&#8221;</p>
<ul>
<li><a href="https://myifew.com/4027/how-to-setup-jenkins-automation-tools/">เริ่มต้นทำ CI/CD – วิธีติดตั้ง Jenkins บน Ubuntu (1)</a></li>
<li><a href="https://myifew.com/4039/automation-testing-by-phpunit-and-jenkins/">เริ่มต้นทำ CI/CD – Automation Testing ด้วย PHPUnit และ Jenkins (2)</a></li>
<li><a href="https://myifew.com/4072/automation-deploy-git-bitbucket-jenkins/">เริ่มต้นทำ CI/CD – Automation Deploy ด้วย Git (Bitbucket) และ Jenkins (3 &#8211; จบ)</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/4072/automation-deploy-git-bitbucket-jenkins/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>เริ่มต้นทำ CI/CD &#8211; Automation Testing ด้วย PHPUnit และ Jenkins (2)</title>
		<link>https://myifew.com/4039/automation-testing-by-phpunit-and-jenkins/</link>
					<comments>https://myifew.com/4039/automation-testing-by-phpunit-and-jenkins/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Thu, 15 Jun 2017 17:10:35 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Automation Testing]]></category>
		<category><![CDATA[Automation Tools]]></category>
		<category><![CDATA[Continuous Delivery]]></category>
		<category><![CDATA[Continuous Deployment]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Jenkins]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=4039</guid>

					<description><![CDATA[Software ที่ใช้ได้และส่งมอบงานได้ไว ก็ต้องมี Automate Test ที่ทำงานได้แทนเรา และครอบคลุม หนึ่งในนั้นคือ Unit Test]]></description>
										<content:encoded><![CDATA[<p>บล็อกที่แล้วเขียนเรื่อง <a href="https://myifew.com/4027/how-to-setup-jenkins-automation-tools/">วิธีติดตั้ง Jenkin</a> ไว้ เพื่อเตรียมทำ CI (Continuous Integration)<br />
แต่ก่อนจะไปถึงตรงนั้น นอกจากต้องใช้ Version Control เป็นแล้ว ก็ต้องมีวิธีการทดสอบโค้ดที่เขียนก่อน</p>
<p>และคุณประโยชน์ที่เราจะไปใช้ Scrum เพื่อทำ Agile คือ การส่งมอบงาน หรือ Software ที่ใช้ได้ ให้ได้ไวๆ<br />
ดังนั้น การส่งมอบให้ได้ไว คือการทำงานเป็นรอบ และการทำงานเป็นรอบ เราจะได้ผลตอบรับไว (fast feedback) ว่าใช่หรือไม่ใช่ ผิดหรือถูก</p>
<p>เช่นกัน Software ที่ใช้ได้และส่งมอบงานได้ไว ก็ต้องมี Automate Test ที่ทำงานได้แทนเรา และครอบคลุม หนึ่งในนั้นคือ Unit Test<span id="more-4039"></span></p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4050" src="https://myifew.com/wp-content/uploads/2017/06/AgileTestingPyramid2.jpg" alt="" width="953" height="580" srcset="https://myifew.com/wp-content/uploads/2017/06/AgileTestingPyramid2.jpg 953w, https://myifew.com/wp-content/uploads/2017/06/AgileTestingPyramid2-600x365.jpg 600w, https://myifew.com/wp-content/uploads/2017/06/AgileTestingPyramid2-768x467.jpg 768w, https://myifew.com/wp-content/uploads/2017/06/AgileTestingPyramid2-700x426.jpg 700w" sizes="auto, (max-width: 953px) 100vw, 953px" /></p>
<p>ว่าแล้วก็ลองมาดูกัน</p>
<h2>ติดตั้ง PHPUnit บน Ubuntu</h2>
<p>ใช้คำสั่งเดียว ดังนี้</p>
<p>[wc_code]$ sudo apt-get install phpunit[/wc_code]</p>
<p>ทดสอบด้วยคำสั่ง</p>
<p>[wc_code]$ phpunit &#8211;version[/wc_code]</p>
<p>จะแสดงข้อความ ประมาณ [wc_code]PHPUnit 3.7.21 by Sebastian Bergmann.[/wc_code] ก็พร้อมใช้แล้วครับ</p>
<h2>ติดตั้ง PHPUnit บน Windows</h2>
<p>ซับซ้อนนิด ลองทำตามดู</p>
<ol>
<li>.ไป Download PHPUnit ที่ https://phpunit.de/</li>
<li>จะได้ไฟล์ชื่อประมาณว่า phpunit-6.2.1.phar ให้เอามันไปไว้ที่ไหนโฟลเดอร์ไหนสักแห่ง ในที่นี้ผมไว้ที่ C:\bin</li>
<li>จากนั้นเข้าไปที่ windows command ด้วยการกด ปุ่มรูป windows ที่ keyboard + R</li>
<li>พิมพ์คำสั่ง<br />
[wc_code]cd C:\bin<br />
echo @php &#8220;%~dp0phpunit-6.2.1.phar&#8221; %* &gt; phpunit.cmd[/wc_code]</li>
<li>ถ้าสังเกตในโฟลเดอร์ของเรา จะพบไฟล์ชื่อ phpunit.cmd ขึ้นมาไฟล์หนึ่ง<br />
<img loading="lazy" decoding="async" class="alignnone size-full wp-image-4042" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_12_12-bin.png" alt="" width="751" height="133" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_12_12-bin.png 751w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_12_12-bin-600x106.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_12_12-bin-700x124.png 700w" sizes="auto, (max-width: 751px) 100vw, 751px" /></li>
<li>จากนั้นไปตั้งค่า Environment Variables และใส่ Path เป็นที่อยู่โฟลเดอร์ที่เราเก็บไฟล์ไว้ นั่นก็คือ C:\bin<br />
<img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4041" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_08_59-bin-1024x659.png" alt="" width="1024" height="659" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_08_59-bin-1024x659.png 1024w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_08_59-bin-600x386.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_08_59-bin-768x495.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_08_59-bin-700x451.png 700w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_08_59-bin.png 1129w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></li>
<li>คราวนี้เราจะสามารถใช้งาน phpunit ที่โฟลเดอร์อื่นๆ นอกเหนือจาก C:\bin ได้แล้ว<br />
<img loading="lazy" decoding="async" class="alignnone size-full wp-image-4040" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_10_34-bin.png" alt="" width="981" height="514" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_10_34-bin.png 981w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_10_34-bin-600x314.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_10_34-bin-768x402.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-18_10_34-bin-700x367.png 700w" sizes="auto, (max-width: 981px) 100vw, 981px" /></li>
</ol>
<h2>เริ่มต้นลองเขียน PHPUnit แบบง่ายๆ</h2>
<p>สมมติว่าผมสร้างไฟล์ชื่อ Calculate.php ในนั้นมี Class ชื่อ Calculate และมี Function บวกเลขขึ้นมาตัวหนึ่ง</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4047" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_04_44-D__www_test_test_ci_cd_src_calculate.php-Notepad.png" alt="" width="595" height="152" /></p>
<p>ผมต้องสร้างไฟล์ทดสอบขึ้นมาประกบ Function นี้ โดยผมตั้งชื่อว่า test_calculate.php และมีโค้ด ดังนี้</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4061" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_07_46-D__www_test_test_ci_cd_test_test_calculate.php-Notepad-1.png" alt="" width="823" height="504" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_07_46-D__www_test_test_ci_cd_test_test_calculate.php-Notepad-1.png 823w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_07_46-D__www_test_test_ci_cd_test_test_calculate.php-Notepad-1-600x367.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_07_46-D__www_test_test_ci_cd_test_test_calculate.php-Notepad-1-768x470.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_07_46-D__www_test_test_ci_cd_test_test_calculate.php-Notepad-1-700x429.png 700w" sizes="auto, (max-width: 823px) 100vw, 823px" /></p>
<p>ไฟล์ นี้ผมจะ include ไฟล์ที่จะทำการทดสอบมาด้วย นั่นคือ Calculate.php<br />
จากนั้นผมจะ extends เรียกใช้ตัว PHPUnit_Framework_TestCase เพื่อเรียกใช้งานคำสั่งต่างๆของ PHPUnit</p>
<p>class Test_calculate extends PHPUnit_Framework_TestCase</p>
<p>เรียกใช้ function setup() ซึ่งมันจะทำคำสั่งทั้งหมดในนี้ทุกครั้งที่มีการทดสอบ function อื่นๆ เสมอ</p>
<p>public function setup() {<br />
$this-&gt;calculate = new Calculate();<br />
}</p>
<p>สร้าง function ทดสอบการบวกเลขขึ้นมา โดยการตั้งชื่อของ PHPUnit จะต้องขึ้นต้นด้วย test เสมอ<br />
และผทใช้คำสั่ง assertEquals() เพื่อตรวจสอบว่า ผลลัพธ์ที่ได้จาก $this-&gt;calculate-&gt;add($a, $b) เท่ากับ $expected หรือไม่<br />
โดยตัวแปล $a, $b, $expected ผมให้เรียกใช้มาจากชุดตัวเลขทดสอบ ที่เก็บไว้ใน function additionProvider()<br />
โดยวิธีการเรียกใช้นั้น คือคำสั่ง @dataProvider ที่อยู่ในกรอบคอมเม้น<br />
/**<br />
* @dataProvider additionProvider<br />
*/<br />
public function testAdd($a, $b, $expected) {<br />
$this-&gt;assertEquals($expected, $this-&gt;calculate-&gt;add($a, $b));<br />
}</p>
<p>public function additionProvider()<br />
{<br />
return [<br />
&#8216;0+0=0&#8217; =&gt; [0, 0, 0],<br />
&#8216;0+1=1&#8217; =&gt; [0, 1, 1],<br />
&#8216;1+0=0&#8217; =&gt; [1, 0, 0],<br />
&#8216;1+4=3&#8217; =&gt; [1, 4, 3]<br />
];<br />
}</p>
<p>จากนั้นทำการรันคำสั่ง</p>
<p>[wc_code]$ phpunit test_calculate.php[/wc_code]</p>
<p>ผลที่ได้จะเป็นดังรูปนี้</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4063" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_08_20.png" alt="" width="827" height="447" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_08_20.png 827w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_08_20-600x324.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_08_20-768x415.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-22_08_20-700x378.png 700w" sizes="auto, (max-width: 827px) 100vw, 827px" /></p>
<p>ความหมายคือ ผลการทดสอบ ไม่ผ่าน<br />
โดยทดสอบด้วย 4 ชุดข้อมูล ไม่ผ่าน 2 ชุดข้อมูล</p>
<h2>ให้ Jenkins ทดสอบ PHPUnit อัตโนมัติ ทุกครั้งที่มีการ Push สู่ Git</h2>
<p>กลับไปที่ Jenkins Project ที่สร้างไว้จากบล็อก <a href="https://myifew.com/4027/how-to-setup-jenkins-automation-tools/">สอนติดตั้ง Jenkins</a><br />
ในส่วนของ Build ให้กดปุ่ม Add build step เพื่อเพิ่มการทำงานในตอน build<br />
จากนั้นเลือกเมนู Excute shell และใส่โค้ดไปว่า</p>
<p>[wc_code]phpunit &#8211;log-junit test/results/phpunit.xml test/test_calculate.php[/wc_code]</p>
<p>ส่วนของ test/results/phpunit.xml จะเป็น path ที่เก็บไฟล์ report, ส่วน test/test_calculate.php คือไฟล์ที่ใช้ทดสอบ<br />
ดังนั้นใครไฟล์ทดสอบไว้อย่างไร ก็แก้กันตามนั้นส่วนไฟล์ report ก็แล้วแต่จะตั้งนะ</p>
<p>ต่อมา ในส่วนของ Post-build Actions ให้กดปุ่ม Add post-build action เพื่อเพิ่มการทำงานหลัง build เสร็จ<br />
จากนั้นเลือกเมนู Publish JUnit test result report และใส่ที่อยู่ของ report เมื่อสักครู่เข้าไป</p>
<p>[wc_code]test/results/phpunit.xml[/wc_code]</p>
<p>(มันจะแจ้งตัวอักษรแดงๆ ว่าไม่พบไฟล์ก็ปล่อยมันไป)</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4052" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_52_30-Test_CI_CD_Bickbucket-Config-Jenkins-1024x595.png" alt="" width="1024" height="595" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_52_30-Test_CI_CD_Bickbucket-Config-Jenkins-1024x595.png 1024w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_52_30-Test_CI_CD_Bickbucket-Config-Jenkins-600x349.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_52_30-Test_CI_CD_Bickbucket-Config-Jenkins-768x446.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_52_30-Test_CI_CD_Bickbucket-Config-Jenkins-1200x697.png 1200w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_52_30-Test_CI_CD_Bickbucket-Config-Jenkins-700x407.png 700w, https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_52_30-Test_CI_CD_Bickbucket-Config-Jenkins.png 1463w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>หลังจากนั้นให้ลองกดปุ่ม Build ดู และให้กดไปที่รายการที่ Build ครั้งล่าสุด</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4053" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-15-23_59_40-Test_CI_CD_Bickbucket-Jenkins.png" alt="" width="355" height="204" /></p>
<p>ให้เลือกเมนู Test Result ก็จะพบผลการทดสอบที่แยก Function การทำงานพร้อม Progress Bar บอกผลด้วยว่า ผ่าน/ไม่ผ่าน กี่ Function</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4054" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_00_29-Test_CI_CD_Bickbucket-7528-Test-Results-Jenkins-1024x414.png" alt="" width="1024" height="414" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_00_29-Test_CI_CD_Bickbucket-7528-Test-Results-Jenkins-1024x414.png 1024w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_00_29-Test_CI_CD_Bickbucket-7528-Test-Results-Jenkins-600x242.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_00_29-Test_CI_CD_Bickbucket-7528-Test-Results-Jenkins-768x310.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_00_29-Test_CI_CD_Bickbucket-7528-Test-Results-Jenkins-1200x485.png 1200w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_00_29-Test_CI_CD_Bickbucket-7528-Test-Results-Jenkins-700x283.png 700w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_00_29-Test_CI_CD_Bickbucket-7528-Test-Results-Jenkins.png 1361w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>ในหน้าของ History ก็จะบอกประวัติของการพบข้อผิดพลาดว่ามากขึ้นหรือลดลง</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4055" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_01_52-History-for-Test-Results-Jenkins-1024x453.png" alt="" width="1024" height="453" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_01_52-History-for-Test-Results-Jenkins-1024x453.png 1024w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_01_52-History-for-Test-Results-Jenkins-600x265.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_01_52-History-for-Test-Results-Jenkins-768x340.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_01_52-History-for-Test-Results-Jenkins-1200x531.png 1200w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_01_52-History-for-Test-Results-Jenkins-700x310.png 700w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_01_52-History-for-Test-Results-Jenkins.png 1347w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>เมื่อไปดูหน้าของ Project ก็จะมี Trend การพบข้อผิดพลาด</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4056" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_03_01-Test_CI_CD_Bickbucket-Jenkins-1024x440.png" alt="" width="1024" height="440" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_03_01-Test_CI_CD_Bickbucket-Jenkins-1024x440.png 1024w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_03_01-Test_CI_CD_Bickbucket-Jenkins-600x258.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_03_01-Test_CI_CD_Bickbucket-Jenkins-768x330.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_03_01-Test_CI_CD_Bickbucket-Jenkins-1200x516.png 1200w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_03_01-Test_CI_CD_Bickbucket-Jenkins-700x301.png 700w, https://myifew.com/wp-content/uploads/2017/06/2560-06-16-00_03_01-Test_CI_CD_Bickbucket-Jenkins.png 1347w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<h2>สรุป</h2>
<p>หลังจากไดทำ Unit Test และไปให้ Jenkins เป็นตัวรันทดสอบให้อัตโนมัติทุกครั้งที่ Commit Code คราวนี้เราก็สบายใจได้แล้วว่า ทุกครั้งที่ทำงานและนำโค้ดขึ้น Git เราจะมีโค้ดที่สามารถทำงานได้ เราได้เริ่มต้นเข้าสู่ Continuous Integration ด้วย Unit Test แล้ววว</p>
<p>ในบล็อกตอนหน้า ผมจะนำโค้ดที่ใช้งานได้บน Git เข้าสู่ Production Server เพื่อลองทำ Continuous Deployment กันครับ</p>
<p>&nbsp;</p>
<p>เว็บอ้างอิง</p>
<ul>
<li><a href="http://www.somkiat.cc/php-continuous-integration-with-jenkins/" target="_blank" rel="noopener noreferrer">สรุปการใช้งาน Jenkins ร่วมกับ PHP project แบบง่ายๆ</a></li>
<li><a href="http://www.somkiat.cc/imrpove-quality-with-continuous-integration/" target="_blank" rel="noopener noreferrer">ปรับปรุงคุณภาพของ Software ด้วย Continuous Integration Process กันนะ</a></li>
<li>รูปปกจาก https://buddy.works/blog/introducing-phpunit</li>
</ul>
<p>ซี่รี่ &#8220;เริ่มต้นทำ CI/CD&#8221;</p>
<ul>
<li><a href="https://myifew.com/4027/how-to-setup-jenkins-automation-tools/">เริ่มต้นทำ CI/CD – วิธีติดตั้ง Jenkins บน Ubuntu (1)</a></li>
<li><a href="https://myifew.com/4039/automation-testing-by-phpunit-and-jenkins/">เริ่มต้นทำ CI/CD – Automation Testing ด้วย PHPUnit และ Jenkins (2)</a></li>
<li><a href="https://myifew.com/4072/automation-deploy-git-bitbucket-jenkins/">เริ่มต้นทำ CI/CD – Automation Deploy ด้วย Git (Bitbucket) และ Jenkins (3 &#8211; จบ)</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/4039/automation-testing-by-phpunit-and-jenkins/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>เริ่มต้นทำ CI/CD &#8211; วิธีติดตั้ง Jenkins บน Ubuntu (1)</title>
		<link>https://myifew.com/4027/how-to-setup-jenkins-automation-tools/</link>
					<comments>https://myifew.com/4027/how-to-setup-jenkins-automation-tools/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Sat, 10 Jun 2017 04:44:50 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Automation Tools]]></category>
		<category><![CDATA[Continuous Delivery]]></category>
		<category><![CDATA[Continuous Deployment]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Jenkins]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=4027</guid>

					<description><![CDATA[Jenkins เป็น Automation Tools ที่เอาไว้ทำอะไรต่างๆแบบอัตโนมัติ ในที่นี้เราจะเอามันไปใช้ทำ CI/CD เพื่อช่วยชีวิตนักพัฒนาอย่างเราให้สบายขึ้น]]></description>
										<content:encoded><![CDATA[<p>ตอนแรกว่าจะเขียนรวดเดียวจบถึงวิธีทำ CI/CD (Continuous Integration and Continuous Deployment) ด้วย Jenkins และ Bitbucket แต่พอเขียน Jenkins จบ รู้สึกว่ายาวไปหน่อย เลยขอตัดเอาเป็น Jenkins ก่อนก็แล้วกันนะ</p>
<p>Jenkins เป็น Automation Tools ที่เอาไว้ทำอะไรต่างๆแบบอัตโนมัติ ซึ่งในที่นี้เราเอามันไปใช้ทำ CI/CD เพื่อช่วยชีวิตนักพัฒนาอย่างเราให้สบายขึ้น อบย่างเช่น นักพัฒนาเพียงแค่เขียนโค้ด นำขึ้น Git แล้วให้ Jenkins ทำการทดสอบจากที่เราตั้งค่าไว้ ไม่ว่าจะ Robot Framework หรือ Test Unit ก็ตาม เมื่อผ่านเรียบร้อย ก็นำกลับไป Merge Code เข้า Git หรือจะ Deploy ต่อไปยัง Server ก็ว่ากันไป (ซึ่งผมจะเขียนบล็อกสอนทำตามนี้แหละ)<span id="more-4027"></span></p>
<p>เนื่องด้วยผมใช้ Linux Ubuntu นะครับ ใครใช้ Linux/Windows อื่นๆ ก็ลองปรับๆเทียบเคียงดูอีกที</p>
<h2>สิ่งที่ต้องมี</h2>
<p>Server ที่ใช้ Ubuntu มี Ram ขั้นต่ำ 1GB จากนั้นให้สร้าง user ที่สามารถใช้งาน sudo ได้และใช้ File Key Authentication ในการเข้าสู่ระบบ (<a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04" target="_blank" rel="noopener noreferrer">วิธีการสร้าง user + file key authentication + disable root user + disable password authentication</a>)</p>
<h2>ติดตั้ง Jenkins</h2>
<p>เพิ่ม repository key ใน server</p>
<p>[wc_code]$ wget -q -O &#8211; https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add -[/wc_code]</p>
<p>พิมพ์ไปแล้วจะแสดงคำว่า [wc_code]OK[/wc_code] เป็นใช้ได้ จากนั้น กำหนด Debian package repo address</p>
<p>[wc_code]$ echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list[/wc_code]</p>
<p>ทำการ update package สักหน่อย</p>
<p>[wc_code]$ sudo apt-get update[/wc_code]</p>
<p>สุดท้ายก็พิมพ์คำสั่งติดตั้ง Jenkins</p>
<p>[wc_code]$ sudo apt-get install jenkins[/wc_code]</p>
<p>จากนั้นให้ทำการ เปิดใช้ Jenkins</p>
<p>[wc_code]$ sudo service jenkins start[/wc_code]</p>
<p>แล้วลองเช็คดูว่า Jenkins ทำงานจริงหรือไม่</p>
<p>[wc_code]$ sudo service jenkins status[/wc_code]</p>
<p>ถ้า Jenkins ทำงานอยู่ ผลลัพธ์ที่ได้จะแสดงประมาณนี้</p>
<p>[wc_code]Jenkins Automation Server is running with the pid 12445[/wc_code] (เลขข้างหลังอะไรก็ว่าไป อาจไม่เหมือนกัน)</p>
<h2>เปลี่ยนเลข Port Jenkins สักหน่อย</h2>
<p>(ถ้าใครไม่ทำตรงนี้ ก็ข้ามไปได้ครับ ใช้ Port 8080 ดังเดิม)</p>
<p>ปกติ Jenkins จะใช้ Port 8080 ซึ่งผมคิดว่าเกร่อไปนิด เลยอยากเปลี่ยนเป็นเลขอื่นๆแทน โดยทำดังนี้</p>
<p>แก้ไข file ชื่อ /etc/default/jenkins</p>
<p>[wc_code]$ sudo vi /etc/default/jenkins[/wc_code]</p>
<p>แก้เลข port บนหาบรรทัดที่เขียนว่า HTTP_PORT</p>
<p>[wc_code]HTTP_PORT=8083[/wc_code]</p>
<p>จากนั้น save แล้วออกมา restart Jenkins อีกรอบ</p>
<p>[wc_code]$ sudo service jenkins restart[/wc_code]</p>
<h2>ตั้งค่าใช้งาน Jenkins</h2>
<p>เมื่อติดตั้งและเปลี่ยน port เรียบร้อย คราวนี้มาตั้งค่ากัน โดยเข้าไปที่ URL เว็บของเราหรือ IP แล้วตามด้วยเลข Port ที่เรากำหนดเมื่อสักครู่</p>
<p>[wc_code]http://ip_address_or_domain_name:8083[/wc_code]</p>
<p>การเข้าใช้งานครั้งแรก จะขึ้นหน้าตาแบบนี้ครับ</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4030" src="https://myifew.com/wp-content/uploads/2017/06/unlock-jenkins-1024x531.png" alt="" width="1024" height="531" srcset="https://myifew.com/wp-content/uploads/2017/06/unlock-jenkins-1024x531.png 1024w, https://myifew.com/wp-content/uploads/2017/06/unlock-jenkins-600x311.png 600w, https://myifew.com/wp-content/uploads/2017/06/unlock-jenkins-768x399.png 768w, https://myifew.com/wp-content/uploads/2017/06/unlock-jenkins-700x363.png 700w, https://myifew.com/wp-content/uploads/2017/06/unlock-jenkins.png 1137w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>ซึ่งมันให้เราเอารหัสเปิดใช้งานครั้งแรกมากรอก โดยพิมพ์คำสั่งดูข้อมูลจาก /var/lib/jenkins/secrets/initialAdminPassword</p>
<p>[wc_code]$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword[/wc_code]</p>
<p>เราจะได้ตัวอักษรผสมตัวเลข 32 หลัก ซึ่งอันนี้แหละ เป็นรหัสที่ใช้กรอก</p>
<p>เมื่อกดไปขั้นต่อไป ให้เลือก Install suggested plugins เป็นค่าเริ่มต้นไปก่อน</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4032" src="https://myifew.com/wp-content/uploads/2017/06/jenkins-customize-1024x531.png" alt="" width="1024" height="531" srcset="https://myifew.com/wp-content/uploads/2017/06/jenkins-customize-1024x531.png 1024w, https://myifew.com/wp-content/uploads/2017/06/jenkins-customize-600x311.png 600w, https://myifew.com/wp-content/uploads/2017/06/jenkins-customize-768x399.png 768w, https://myifew.com/wp-content/uploads/2017/06/jenkins-customize-700x363.png 700w, https://myifew.com/wp-content/uploads/2017/06/jenkins-customize.png 1137w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>แล้วมันจะทำการติดตั้งอะไรก็ว่าไป จนไปถึงหน้ากรอกข้อมูล Admin User ก็ตั้งได้จตามสะดวกที่ต้องการ เป็นอันเสร็จพิธี และเมื่อกดปุ่ม Start using Jenkins จะเจอหน้าแรกประมาณนี้ (Test Project กับ Test CI CD (Bitbucket) ไม่ต้องไปสนใจมันนะฮะ พอดีผมลืมแคปเจอร์หน้าตอนที่ยังไม่มี Project ขึ้น แหะๆ)</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-4034" src="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-11_21_03-Dashboard-Jenkins-1024x545.png" alt="" width="1024" height="545" srcset="https://myifew.com/wp-content/uploads/2017/06/2560-06-10-11_21_03-Dashboard-Jenkins-1024x545.png 1024w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-11_21_03-Dashboard-Jenkins-600x319.png 600w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-11_21_03-Dashboard-Jenkins-768x409.png 768w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-11_21_03-Dashboard-Jenkins-1200x639.png 1200w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-11_21_03-Dashboard-Jenkins-700x372.png 700w, https://myifew.com/wp-content/uploads/2017/06/2560-06-10-11_21_03-Dashboard-Jenkins.png 1915w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>&nbsp;</p>
<p>เว็บอ้างอิง</p>
<ul>
<li><a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04" target="_blank" rel="noopener noreferrer">Initial Server Setup with Ubuntu 16.04</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-install-jenkins-on-ubuntu-16-04" target="_blank" rel="noopener noreferrer">How To Install Jenkins on Ubuntu 16.04</a></li>
<li><a href="https://stackoverflow.com/questions/15265277/how-to-start-jenkins-on-different-port-rather-than-8080-using-command-prompt-in" target="_blank" rel="noopener noreferrer">How to start Jenkins on different port rather than 8080 using command prompt in Windows?</a></li>
<li>รูปปกจาก http://www.drdobbs.com/testing/soasta-and-cloudbees-team-for-jenkins-pl/240152753</li>
</ul>
<p>ซี่รี่ &#8220;เริ่มต้นทำ CI/CD&#8221;</p>
<ul>
<li><a href="https://myifew.com/4027/how-to-setup-jenkins-automation-tools/">เริ่มต้นทำ CI/CD – วิธีติดตั้ง Jenkins บน Ubuntu (1)</a></li>
<li><a href="https://myifew.com/4039/automation-testing-by-phpunit-and-jenkins/">เริ่มต้นทำ CI/CD – Automation Testing ด้วย PHPUnit และ Jenkins (2)</a></li>
<li><a href="https://myifew.com/4072/automation-deploy-git-bitbucket-jenkins/">เริ่มต้นทำ CI/CD – Automation Deploy ด้วย Git (Bitbucket) และ Jenkins (3 &#8211; จบ)</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/4027/how-to-setup-jenkins-automation-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
