<?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>.Net Core &#8211; Few Steps &#8211; ก้าวสั้นๆ แต่ไปเรื่อยๆ</title>
	<atom:link href="https://myifew.com/tag/net-core/feed/" rel="self" type="application/rss+xml" />
	<link>https://myifew.com</link>
	<description></description>
	<lastBuildDate>Mon, 11 May 2020 03:51:41 +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>.Net Core &#8211; Few Steps &#8211; ก้าวสั้นๆ แต่ไปเรื่อยๆ</title>
	<link>https://myifew.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>ทดสอบความเร็ว .NET Core 3.1 (Official Runtimes) บน AWS Lambda</title>
		<link>https://myifew.com/5712/benchmark-netcore-31-aws-lambda/</link>
					<comments>https://myifew.com/5712/benchmark-netcore-31-aws-lambda/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Sun, 10 May 2020 18:55:07 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWS Lambda]]></category>
		<category><![CDATA[Performance Test]]></category>
		<category><![CDATA[Serverless]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=5712</guid>

					<description><![CDATA[วันที่ผมรอคอยก็มาถึง เมื่อ AWS ประกาศรองรับ .NET Core 3.1 เป็น Official Runtime ทำให้ Cold Start ลดลงกว่า 50%]]></description>
										<content:encoded><![CDATA[
<p>และแล้ว วันที่ผมรอคอย (รวมถึงชาว .NET Core บน. AWS Lambda) ก็มาถึง เมื่อ <a rel="noreferrer noopener" href="https://aws.amazon.com/blogs/compute/announcing-aws-lambda-supports-for-net-core-3-1/" target="_blank">AWS ประกาศรองรับ .NET Core 3.1 เป็น Official Runtime</a> แล้วจ้า (จริงๆ ประกาศตั้งแต่ 31 มีนาคม 2563 แต่ผมเห็นช้าไปหน่อย แหะๆ) ต้องขอทดสอบสักหน่อยว่าดีขึ้นแค่ไหน แต่เฉลยไว้ก่อนเลยว่า ลดเวลาการเกิด Cold Start จาก .NET Core 2.1 ได้ถึง 50% เลยทีเดียว</p>



<span id="more-5712"></span>



<p>ผมเคยเขียนในบล็อกเรื่อง <a href="https://myifew.com/5565/benchmark-netcore-3-and-readytorun-for-aws-lambda/">ทดสอบความเร็ว .NET Core 3.0 และฟีเจอร์ ReadyToRun (R2R) ในการทำ AWS Lambda</a> ซึ่งตอนนั้น ได้ทำเป็น Custom Runtime และผลลัพธ์ที่ได้ .NET Core 3.0 เร็วกว่า 2.1 นิดเดียว แทบไม่เห็นความต่าง ดีขึ้นแค่เสถียร เพราะมันไปช้าที่ AWS Custom Library ดังนั้นผมจึงแนะนำให้ทุกคนไปใช้ <a rel="noreferrer noopener" href="https://github.com/zaccharles/lambda-native" target="_blank">LambdaNative</a> แทน แต่ก็ยังมีปัญหาว่า <a rel="noreferrer noopener" href="https://github.com/dotnet/corert/issues/7386" target="_blank">Microsoft.DotNet.ILCompiler</a>&nbsp;ไม่รองรับการทำงานที่ซับซ้อน อย่างการเรียกใช้งาน Database ร่วมกับ EFCore หรือ Dapper จึงทำให้ต้องเลี่ยงไปใช้ต่อตรงด้วย ADO.NET  แทน</p>



<h2 class="wp-block-heading">ปัจจัยที่ทำให้ .NET Core 3.1 ทำงานได้ไวใน AWS Lambda</h2>



<p>จากที่ทดสอบเอง, ดู Benchmark, และอ่านๆ จากหลายแหล่ง พบว่ามีหลักๆ มี 2 เรื่อง ที่เป็นฟีเจอร์ใหม่ใน .NET Core 3 ที่ช่วยให้ AWS Lambda ทำงานได้ไวขึ้น คือ </p>



<ol class="wp-block-list"><li><strong>Built-in JSON support</strong> : ซึ่งปกติผู้เขียน C# เวลาต้องแปลง Object, Array ออกไปเป็น JSON จะต้องใช้ Library อย่างเช่น Newtonsoft.Json ซึ่งมันดีนะ แต่มันก็คือการจัดการ String บน .NET แบบ UTF-16 ซึ่งจะเปลืองทรัพยากรมากในการ convert ไปๆมาๆ<br><br>แต่สำหรับ .NET Core 3.0 เป็นต้นมา ได้ built-in JSON Converter เข้าไปเลย และทำงานแบบ UTF-8 encoder ตั้งแต่ต้น จึงไวขึ้น และเมื่อ Core มันมี JSON Converter มาแล้ว ก็ไม่ต้องเรียก 3rd Party Library จึงทำให้ AWS Lambda เราตัวเล็กลงด้วย (ดูการเปรียบเทียบ <a rel="noreferrer noopener" href="https://michaelscodingspot.com/the-battle-of-c-to-json-serializers-in-net-core-3/" target="_blank">The Battle of C# to JSON Serializers in .NET Core 3</a>)</li><li><strong>ReadyToRun</strong> :  เป็นฟีเจอร์ที่ Microsoft ภูมิใจนำเสนอมา ตั้งแต่ปล่อยตัว .NET Core 3.0 Preview 6 แล้ว นั่นคือทำให้ .NET Core application แปลงเป็น Native App ส่งผลให้มีการทำงานที่ไวขึ้น มี startup time ที่ดีขึ้น (AOT – ahead-of-time) เพราะไปลดปริมาณงานที่ต้องแปลง Byte Code เป็น Executable Code ลง (JIT – Just in Time)</li></ol>



<p>ดังนั้น แม้ว่า 3.1 จะเร็วขึ้นมาบ้าง แต่ก็ยังแนะนำให้ทำ AWS Lambda เป็น ReadyToRun นะครับ</p>



<h2 class="wp-block-heading">การ Migrate ไปใช้ .NET Core 3.1 Runtime บน AWS Lambda</h2>



<p>แก้แค่ 3 จุดหลักๆ (ส่วนโค้ดอื่นๆ ที่ไม่รองรับหลังจากทำ 3+1 ข้อนี้ ก็ไปตามแก้กันต่ออีกนะ)</p>



<h4 class="wp-block-heading">1. เปลี่ยน Target Framework ใหม่</h4>



<pre class="wp-block-code"><code>&lt;TargetFramework>netcoreapp3.1&lt;/TargetFramework></code></pre>



<h4 class="wp-block-heading">2. <strong>เปลี่ยนการเรียกใช้ JSON ใหม่</strong></h4>



<p>ใช้ JSON Package ใหม่ชื่อ</p>



<pre class="wp-block-code"><code>Amazon.Lambda.Serialization.SystemTextJson</code></pre>



<p>และระบุใน Class Handler ใหม่เป็น</p>



<pre class="wp-block-code"><code>&#91;assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]</code></pre>



<h4 class="wp-block-heading">3. ใน Deployment Config เปลี่ยน Function Runtime ใหม่ (aws-lambda-tools-defaults.json)</h4>



<pre class="wp-block-code"><code>"framework" : "netcoreapp3.1",
"function-runtime":"dotnetcore3.1",</code></pre>



<h4 class="wp-block-heading">ในกรณี ต้องการทำเป็น ReadyToRun จะต้องเพิ่มใน Csproj file ด้วยดังนี้</h4>



<pre class="wp-block-code"><code>&lt;PublishReadyToRun>true&lt;/PublishReadyToRun>
&lt;TieredCompilation>false&lt;/TieredCompilation>
&lt;TieredCompilationQuickJit>false&lt;/TieredCompilationQuickJit></code></pre>



<p>ที่ต้องปิด Tiered Compilation เพราะจะเกิด Startup Time สูงขึ้น และใช้ Memory เยอะขึ้นโดยใช่เหตุ (อ้างอิง Performance จาก <a href="https://medium.com/@zaccharles/net-core-3-0-aws-lambda-benchmarks-and-recommendations-8fee4dc131b0">.NET Core 3.0 AWS Lambda Benchmarks and Recommendations</a> และหากใครต้องการเข้าใจ <a href="https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#tiered-compilation">What&#8217;s new in .NET Core 3.0</a>)</p>



<p>และถ้าจะใช้ ReadyToRun จะต้อง Build และ Publish เป็น Linux Runtime นะครับ ไม่สามารถ Build Image บน Windows/Mac แล้วเอาขึ้นไปทำงานบน AWS Lambda ได้ เนื่องจากบนนั้นจะเป็น Amazon Linux 2 (ไปดูลิงค์ Demo ของผมที่ด้านล่างได้ ว่าทำอย่างไร)</p>



<h2 class="wp-block-heading">ผลทดสอบ Cold Start ของ AWS Lambda .NET Core 2.1 เมื่อเทียบกับ 3.1 และ 3.1 ReadyToRun</h2>



<p>ผมใช้โค้ดทดสอบตัวเดิมกับที่เคยทดสอบในบล็อก <a href="https://myifew.com/5565/benchmark-netcore-3-and-readytorun-for-aws-lambda/">ทดสอบความเร็ว .NET Core 3.0 และฟีเจอร์ ReadyToRun (R2R) ในการทำ AWS Lambda</a> ซึ่งได้ลองรัน 2.1 ก่อน จากนั้น migrate ไปเป็น 3.1 เพื่อเก็บข้อมูล และสุดท้ายก็ Compile ไปเป็น 3.1 ReadyToRun เพื่อเก็บข้อมูล</p>



<p>โดยการทำงานของโค้ดจะมีการเชื่อมต่อ MySQL Database 5.6 โดยมี table ชื่อ members ที่มี 3 รายการ (ค่าจะผันผวนบ้าง อาจเกิดจากการเรียกใช้ MySQL Database)</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1602" height="374" src="https://myifew.com/wp-content/uploads/2020/05/compare-aws-lambda-netcore21-31-31r2r.png" alt="" class="wp-image-5715"/></figure>



<p>จะสังเกตได้ว่า แค่แปลงไปใช้ Runtime 3.1 และ Built-in JSON ก็ลดเวลาการเกิด Cold Start ลงไปหนึ่งเท่าตัวเลยทีเดียว </p>



<p>และเมื่อทำเป็น 3.1 ReadyToRun ก็เร็วขึ้นอีกราวๆ 100ms และเมื่อเพิ่ม config เพื่อปิดการใช้ Tiered Compilation ก็ทำให้เร็วขึ้นไปอีก 100-300ms เมื่อใช้ Memory 128MB และ 256MB</p>



<p>แต่มีข้อสังเกตว่า การทำ ReadyToRun มีความเสถียรขึ้น รันครั้งที่ 2 เป็นต้นไป ไม่เจอว่าเกิน 100ms เลย (อย่าลืมว่า AWS Lambda คิดเงินทุกๆ 100ms ดังนั้นเมื่อเกิน มันจะปัดเศษขึ้นไปเป็นอีก 100ms) ส่วนความเร็วที่ผันผวนขึ้นลง 3-5ms ผมคิดว่ามีความต่างน้อยมากๆ ไม่จำเป็นต้องใส่ใจก็ได้ ถ้างานเราไม่ได้ซีเรียสกับเวลาแค่นี้</p>



<p>และเมื่อเราไม่ใช้ LambdaNative กับ <a rel="noreferrer noopener" href="https://github.com/dotnet/corert/issues/7386" target="_blank">Microsoft.DotNet.ILCompiler</a>&nbsp; ตัวปัญหาแล้ว มันจึงทำให้เราใช้ Library ต่างๆ อย่าง Dapper ได้แล้วครับ ไม่ต้องใช้ ADO มา  Mapping Object เองอีกต่อไป แต่ก็ต้องแลกกับเวลาที่มากขึ้นอีก 15-30%</p>



<figure class="wp-block-image size-large"><img decoding="async" width="698" height="346" src="https://myifew.com/wp-content/uploads/2020/05/compare-aws-lambda-netcore31r2r-dapper.png" alt="" class="wp-image-5716"/></figure>



<h2 class="wp-block-heading">สรุป</h2>



<p>ใครใช้ .NET Core 2.1 บน AWS Lambda อยู่ หรือใช้ LambdaNative หรือใช้ Custom Runtime ตามที่ผมเคยเขียนถึง แนะนำให้เปลี่ยนมาใช้ .NET Core 3.1 ของ AWS Official Runtime ครับ ไวกว่ามาก และถ้าเป็นไปได้ ก็ควรทำให้เป็น ReadyToRun Image ด้วย เพื่อประสิทธิภาพที่ดีขึ้นครับ</p>



<p>ส่วนใครอยากลองดูโค้ดตัวอย่าง ดูได้จากด้านล่างนี้เลย</p>



<p><strong>Code Demo</strong></p>



<ul class="wp-block-list"><li><a rel="noreferrer noopener" href="https://github.com/ifew/aws-lambda-db" target="_blank">AWS Lambda .NET Core 3.1 แบบธรรมดา</a></li><li><a rel="noreferrer noopener" href="https://github.com/ifew/aws-lambda-db-r2r" target="_blank">AWS Lambda .NET Core 3.1 แบบทำเป็น ReadyToRun</a></li><li><a rel="noreferrer noopener" href="https://github.com/ifew/aws-lambda-dapper-r2r" target="_blank">AWS Lambda .NET Core 3.1 แบบทำเป็น ReadyToRun และใช้ Dapper ในการเรียกใช้งาน MySQL Database</a></li></ul>



<p><strong>อ้างอิง</strong></p>



<ul class="wp-block-list"><li><a href="https://aws.amazon.com/blogs/compute/announcing-aws-lambda-supports-for-net-core-3-1/">https://aws.amazon.com/blogs/compute/announcing-aws-lambda-supports-for-net-core-3-1/</a></li><li><a href="https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0">https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0</a></li><li><a href="https://medium.com/@zaccharles/a-close-look-at-net-core-3-1-on-aws-lambda-9ccec4dd96be">https://medium.com/@zaccharles/a-close-look-at-net-core-3-1-on-aws-lambda-9ccec4dd96be</a></li><li><a href="https://vincepergolizzi.com/programming/2020/04/01/dotnet-core-3.1-lambda.html">https://vincepergolizzi.com/programming/2020/04/01/dotnet-core-3.1-lambda.html</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/5712/benchmark-netcore-31-aws-lambda/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ทดสอบความเร็ว .NET Core 3.0  และฟีเจอร์ ReadyToRun (R2R) ในการทำ AWS Lambda</title>
		<link>https://myifew.com/5565/benchmark-netcore-3-and-readytorun-for-aws-lambda/</link>
					<comments>https://myifew.com/5565/benchmark-netcore-3-and-readytorun-for-aws-lambda/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Tue, 01 Oct 2019 19:20:35 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWS Lambda]]></category>
		<category><![CDATA[Performance Test]]></category>
		<category><![CDATA[Serverless]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=5565</guid>

					<description><![CDATA[เมื่อ 23 กันยายน ที่ผ่านมา Microsoft ได้ฤกษ์เปิดตัว .NET Core 3.0 (3.0.100) ตัวเต็มให้ได้เล่นกัน หลังจากเป็นข่าวมาร่วมปี ซึ่งมาพร้อมกับฟีเจอร์และพัฒนา Performance จากเวอร์ชัน 2.2 เยอะพอสมควร ใครอยากรู้รายละเอียดสามารถไปตามอ่านได้ที่&#8230;]]></description>
										<content:encoded><![CDATA[
<p>เมื่อ 23 กันยายน ที่ผ่านมา <a rel="noreferrer noopener" aria-label="Microsoft ได้ฤกษ์เปิดตัว .NET Core 3.0 (opens in a new tab)" href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0/" target="_blank">Microsoft ได้ฤกษ์เปิดตัว .NET Core 3.0</a> (3.0.100) ตัวเต็มให้ได้เล่นกัน หลังจากเป็นข่าวมาร่วมปี ซึ่งมาพร้อมกับฟีเจอร์และพัฒนา Performance จากเวอร์ชัน 2.2 เยอะพอสมควร ใครอยากรู้รายละเอียดสามารถไปตามอ่านได้ที่ <a rel="noreferrer noopener" aria-label="Announcing .NET Core 3.0 (opens in a new tab)" href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0/" target="_blank">Announcing .NET Core 3.0</a></p>



<p>ในครั้งนี้มีฟีเจอร์หนึ่งที่น่าสนใจคือ ReadyToRun (R2R) ซึ่งจริงๆ มันมาตั้งแต่ .NET Core 3 Preview 6 แล้ว นั่นคือทำให้ .NET Core application แปลงเป็น Native App ให้ไวขึ้น ส่งผลกับ startup time ที่ดีขึ้น (AOT &#8211; ahead-of-time) จากแต่เดิมซึ่งมีเพียงแค่ แปลง Byte Code เป็น Executable Code  (JIT &#8211; Just in Time)</p>



<p>ผมนี่ตาลุกวาวทันที ไม่รอช้าที่จะหยิบมันมาทดสอบเป็น AWS Lambda เพื่อดูปัญหา Cold Start ว่าจะดีขึ้นได้มากน้อยแค่ไหน</p>



<span id="more-5565"></span>



<h2 class="wp-block-heading">เริ่มต้นใช้ ReadyToRun (How to Use)</h2>



<p>วิธีการใช้ ReadyToRun  ไม่ยาก แค่ตั้ง Config ในไฟล์ csproj ได้เลย ตัวอย่างที่ผมใช้จะประมาณนี้</p>



<pre class="wp-block-code"><code>&lt;PropertyGroup>
    &lt;OutputType>Exe&lt;/OutputType>
    &lt;TargetFramework>netcoreapp3.0&lt;/TargetFramework>
    &lt;LangVersion>latest&lt;/LangVersion>
    &lt;AWSProjectType>Lambda&lt;/AWSProjectType>
    &lt;PublishReadyToRun>true&lt;/PublishReadyToRun>
    &lt;PublishTrimmed>true&lt;/PublishTrimmed>
    &lt;PublishSingleFile>true&lt;/PublishSingleFile>
&lt;/PropertyGroup></code></pre>



<p>Config R2R ที่ใช้บ่อย จะมี ดังนี้</p>



<ul class="wp-block-list"><li><strong>PublishReadyToRun</strong>&nbsp;คือ บอกให้แอพเรา Publish เป็น ReadyToRun Image</li><li><strong>PublishTrimmed</strong>&nbsp;คือ ตัด assemblies ที่ไม่ได้ใช้ออกไป ซึ่งจะช่วยลดขนาดไฟล์ได้ (โดยจะวิเคราะห์ IL จาก Library ที่ชื่อว่า&nbsp;<a rel="noreferrer noopener" href="https://github.com/mono/linker" target="_blank">IL linker</a>&nbsp;สามารถไปดู Docs เพื่อกำหนดค่าต่างๆได้)</li><li><strong>PublishSingleFile</strong>&nbsp;คือ บอกว่าจะ Publish โดยแพ็คมาให้เป็น Executable File ไฟล์เดียว (ดูข้อมูลเพิ่มเติมได้ที่ <a href="https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md" target="_blank" rel="noreferrer noopener" aria-label="design (opens in a new tab)">design</a>)</li></ul>



<p>และเวลา publish จะต้องระบุ RuntimeIdentifier ด้วย เช่น</p>



<pre class="wp-block-code"><code>dotnet publish -r win-x64 -c Release</code></pre>



<p>ดู Runtime เพิ่มเติมได้ที่ <a rel="noreferrer noopener" aria-label=".NET Core Runtime IDentifier (RID) catalog (opens in a new tab)" href="https://docs.microsoft.com/en-us/dotnet/core/rid-catalog" target="_blank">.NET Core Runtime IDentifier (RID) catalog</a></p>



<h2 class="wp-block-heading">วิธีการทดสอบ (Test Scenarios)</h2>



<p>ผมเคยทดสอบ AWS Lambda เพื่อแก้ปัญหา Cold Start ด้วยหลายๆ วิธี (อ่านเพิ่มเติมได้จาก <a rel="noreferrer noopener" aria-label="9 วิธีจัดการกับ Cold Start ใน Serverless ให้ทำงานได้ไวที่สุด พร้อมตัวอย่าง (opens in a new tab)" href="https://myifew.com/5386/9-ways-to-fighting-with-cold-start-in-serverless/" target="_blank">9 วิธีจัดการกับ Cold Start ใน Serverless ให้ทำงานได้ไวที่สุด พร้อมตัวอย่าง</a>) ครั้งนี้ผมจึงอ้างอิงจากโค้ดชุดเดิม โดยจะทดสอบ 3 Scenarios ดังนี้</p>



<ol class="wp-block-list"><li>ชุดโค้ด .NET Core Dll file ปกติ ที่เปลี่ยน Version จาก AWS Standard Runtime .NET Core 2.1 มาเป็นทำ Custom Runtime ขึ้นมาเองด้วย .NET Core 3.0.100 ผ่านไลบรารี่ <a rel="noreferrer noopener" aria-label="Amazon.Lambda.RuntimeSupport (opens in a new tab)" href="https://aws.amazon.com/blogs/developer/announcing-amazon-lambda-runtimesupport/" target="_blank">Amazon.Lambda.RuntimeSupport</a></li><li>ชุดโค้ด .NET Core Native ที่ใช้ไลบรารี่ <a rel="noreferrer noopener" aria-label="LambdaNative (opens in a new tab)" href="https://github.com/zaccharles/lambda-native" target="_blank">LambdaNative</a> และ <a rel="noreferrer noopener" aria-label="Microsoft.DotNet.ILCompiler (opens in a new tab)" href="https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.DotNet.ILCompiler" target="_blank">Microsoft.DotNet.ILCompiler</a> แต่เปลี่ยน Version จากเดิม Custom Runtime .NET Core 2.2 มาเป็น Custom Runtime .NET Core 3.0.100</li><li>ชุดโค้ด .NET Core Native ที่ใช้ไลบรารี่ <a rel="noreferrer noopener" href="https://github.com/zaccharles/lambda-native" target="_blank">LambdaNative</a> อย่างเดียว แต่เปลี่ยน Version จากเดิม Custom Runtime .NET Core 2.2 มาเป็น Custom Runtime .NET Core 3.0.100 โดยจะ Publish เป็น ReadyToRun  Image</li></ol>



<p>Database ผมใช้ MySQL 5.5 ที่อยู่บน AWS RDS วงเดียวกันกับ AWS Lambda ที่ผมทดสอบ และจำนวนข้อมูลมีเพียง 3 Rows</p>



<figure class="wp-block-image"><img decoding="async" width="1608" height="1052" src="https://myifew.com/wp-content/uploads/2019/10/lambda-test-data-3rows.png" alt="" class="wp-image-5570"/></figure>



<h2 class="wp-block-heading">ผลการทดสอบ (Benchmark Results)</h2>



<h4 class="wp-block-heading">Scenario 1 &#8211; Normal Compile with Custom Runtime .NET Core 2.2 กับ .NET Core 3.0.100 โดยใช้ ADO ในการเชื่อมต่อกับ MySQL</h4>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1070" height="346" src="https://myifew.com/wp-content/uploads/2019/10/benchmark-crt-ado-netcore22-vs-netcore3.png" alt="" class="wp-image-5571"/></figure>



<p>จะสังเกตว่า .NET Core 3.0 ภาพรวมแล้ว เวลาของ Cold Start จะไวกว่า 2.2 เมื่อใช้ Memory 256Mb และ 512Mb  แต่เมื่อเรียกใช้งานซ้ำๆ .NET Core 3.0 ทำงานได้ไวกว่าทุกกรณี</p>



<p>คราวนี้ลองมาดูเทียบ .NET Core 3.0.100 ด้วยกัน แบบที่ใช้ ReadyToRun กับแบบไม่ใช้</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1106" height="346" src="https://myifew.com/wp-content/uploads/2019/10/benchmark-crt-ado-netcore3-vs-netcore3-readytorun.png" alt="" class="wp-image-5572"/></figure>



<p>ผลคือ เมื่อทำเป็น ReadyToRun จะมีความเร็วของ Cold Start มากกว่าเดิม 50% แบบชนะขาดลอย  ส่วนการเรียกใช้งานแบบซ้ำๆ ตรงนี้ เลขที่ผมได้ ยังเหวี่ยงๆ แต่โดยรวมถือว่าดีกว่า</p>



<p>มาลองเทียบ .NET Core 3.0.100 แบบที่ใช้ ReadyToRun แต่ใช้ Library Connect MySQL ต่างๆ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1686" height="346" src="https://myifew.com/wp-content/uploads/2019/10/benchmark-crt-ado-dapper-efcore-netcore3-readytorun.png" alt="" class="wp-image-5573"/></figure>



<p>ผลตามคาด คล้ายกับบล็อกเดิมที่ผมเคยทดสอบ คือ ADO เร็วที่สุด รองมาเป็น Dapper และช้าสุดคือ EFCore แต่ทั้งนี้ ผมจะทดสอบใหม่อีกครั้งเมื่อ EFCore 3.0 รองรับการใช้งาน MySQL (ซึ่งตอนที่ผมเขียนบล็อกนี้ รองรับ MySQL แค่ EFCore 2.2 อยู่นะครับ)</p>



<p>และให้สังเกต ว่าตรง memory 128 ผมมีคำว่า (max) อยู่ หมายถึงว่า .NET Core 3.0 กิน memory มากขึ้นจน Lambda ใช้เกิน Memory 128Mb</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="924" height="77" src="https://myifew.com/wp-content/uploads/2019/10/lambda-memory-peak.png" alt="" class="wp-image-5574"/><figcaption>ขึ้นแสดงแบบนี้ หมายถึงใช้ Memory มากกว่าที่จัดสรรให้ ตรงนี้ผมไม่แน่ใจว่ากระทบเงินอย่างไร แต่ที่เห้นชัดคือ ส่งผลทำให้ process ช้าลงพอสมควร น่าจะเกิดจากการต้องใช้และรอคืน memory ออกมาทำงาน จนกว่าจะเสร็จ process ทั้งหมด</figcaption></figure>



<h4 class="wp-block-heading">Scenario 2 &#8211; .NET Core 3.0.100 with LambdaNative and Microsoft.DotNet.ILCompiler โดยใช้ ADO ในการเชื่อมต่อกับ MySQL</h4>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1050" height="378" src="https://myifew.com/wp-content/uploads/2019/10/benchmark-lambdanative-ado-netcore22-vs-netcore3.png" alt="" class="wp-image-5577"/></figure>



<p>ที่ผมเคยเขียนถึง .NET Core 2.2 และ LambdaNative ไว้ใน <a rel="noreferrer noopener" href="https://myifew.com/5386/9-ways-to-fighting-with-cold-start-in-serverless/" target="_blank">9 วิธีจัดการกับ Cold Start ใน Serverless ให้ทำงานได้ไวที่สุด พร้อมตัวอย่าง</a> เป็นวิธีการที่แก้ปัญหา Cold Start ได้ดีที่สุด แต่เมื่อทดสอบกับ .NET Core 3.0 ด้วยแล้วปรากฎว่า ผมลัพธ์ดีขึ้นกว่าแต่เดิม และความเร็วเสถียรพอกันทุกครั้งที่มีการเรียกซ้ำๆ เป็นที่น่าพอใจมาก</p>



<h4 class="wp-block-heading">Scenario 3 &#8211; .NET Core 3.0.100 with LambdaNative and ReadyToRun โดยใช้ ADO ในการเชื่อมต่อกับ MySQL</h4>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1566" height="382" src="https://myifew.com/wp-content/uploads/2019/10/benchmark-lambdanative-ado-netcore3-readytorun.png" alt="" class="wp-image-5579"/></figure>



<p>ลองใช้ LambdaNative ร่วมกับ ReadyToRun ปรากฎว่าทำงานได้ช้ากว่า ใช้ไลบรารี่ Microsoft.DotNet.ILCompiler เสียอีก</p>



<p>การทำงานของ ReadyToRun จะมีการ Extract Bundle Files (ดูข้อมูลเพิ่มเติมได้ที่ <a rel="noreferrer noopener" aria-label="Bundler (opens in a new tab)" href="https://github.com/dotnet/designs/blob/master/accepted/single-file/extract.md" target="_blank">bundler</a>) ก่อนทำการ Execution ผมจึงลองทดสอบแบบทำเป็น Single File ที่เอา Bundle รวมกับโค้ด  (<strong>PublishSingleFile</strong> เป็น true) และแบบ Multiple File ที่เอา Bundle แยกกับโค้ด  (<strong>PublishSingleFile</strong> เป็น false) มาเทียบกัน ปรากฎว่า แบบ Single File ทำงานได้เร็วกว่า (น่าจะมาจาก ahead-of-time ใน .NET Core 3.0 นี้)</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1986" height="380" src="https://myifew.com/wp-content/uploads/2019/10/benchmark-lambdanative-ado-netcore22-vs-netcore3-readytorun.png" alt="" class="wp-image-5578"/></figure>



<p>ถ้าลองเทียบทั้งหมดจาก Scenario 2 และ Scenario 3 พบว่า .NET Core 3 NativeLambda ที่ทำงานกับ Microsoft.DotNet.ILCompiler จะทำงานได้ไวที่สุด</p>



<p>คราวนี้ลองถ้าลองเปลี่ยนตัว Connect MySQL มาเป็น Dapper ดูบ้าง ผลลัพธ์จะต่างกันขนาดไหน</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1988" height="876" src="https://myifew.com/wp-content/uploads/2019/10/benchmark-lambdanative-ado-dapper-netcore3-readytorun.png" alt="" class="wp-image-5580"/></figure>



<p>ในส่วนของ LambdaNative ที่ทำงานกับ <a rel="noreferrer noopener" aria-label="Microsoft.DotNet.ILCompiler ปัจจุบันยังไม่รองรับ Dapper เช่นเดิม (opens in a new tab)" href="https://github.com/dotnet/corert/issues/7386" target="_blank">Microsoft.DotNet.ILCompiler ปัจจุบันยังไม่รองรับ Dapper เช่นเดิม</a> ดังนั้นจึงมีแต่ผลทดสอบของ LambdaNative ที่ทำงานกับ ReadyToRun ซึ่งผลลัพธ์ที่ได้ Cold Start จะช้ากว่า ADO ประมาณ 0.5-2 วินาที เลยทีเดียว</p>



<h2 class="wp-block-heading">สรุป</h2>



<p>จากผลลัพธ์ทั้งหมดที่ลองทดสอบ ฟันธงได้ว่า .NET Core 3.0 ทำงานได้ไวกว่าเวอร์ชันเก่า  ในการนำมาทำ Serverless อย่าง AWS Lambda </p>



<p>แต่ด้วยความที่เป็นของใหม่ ผมเชื่อว่าคงมีอะไรปล่อยออกมาอีกเรื่อยๆ ดังนั้น ขอแนะนำชาว .NET Core ว่ามันคุ้มค่าแก่การ migrate ไปเป็น 3.0 ครับ (ข่าวว่า .NET Core 3.1 จะออกมาในช่วงพฤศจิกายนนี้)</p>



<p>บล็อกนี้ พอเห็น Release ปุ๊บรีบทดสอบและเขียนไว้ทันที ใหม่จนยังไม่มีใครเขียนถึงเลย หากผิดพลาดประการใด สามารถแนะนำ/ติชมได้นะครับ</p>



<h2 class="wp-block-heading">Demo Source Code</h2>



<ul class="wp-block-list"><li><a href="https://github.com/ifew/netcore3-readytorun-efcore">netcore3-readytorun-efcore</a> &#8211; ทดสอบ .NET Core 3 ReadyToRun กับ EFCore</li><li><a href="https://github.com/ifew/netcore3-readytorun-dapper">netcore3-readytorun-dapper</a> &#8211; ทดสอบ .NET Core 3 ReadyToRun กับ Dapper</li><li><a href="https://github.com/ifew/aws-lambda-netcore3-db">aws-lambda-netcore3-db</a> &#8211; ทดสอบทำ Lambda ด้วย .NET Core 3 กับ ADO</li><li><a href="https://github.com/ifew/aws-lambda-netcore3-efcore">aws-lambda-netcore3-efcore</a> &#8211; ทดสอบทำ Lambda ด้วย .NET Core 3 กับ EFCore</li><li><a href="https://github.com/ifew/aws-lambda-netcore3-dapper">aws-lambda-netcore3-dapper</a> &#8211; ทดสอบทำ Lambda ด้วย .NET Core 3 กับ Dapper</li><li><a href="https://github.com/ifew/aws-lambda-lambdanative-db">aws-lambda-lambdanative-db</a> &#8211; ทดสอบทำ LambdaNative ด้วย .NET Core 3 กับ ADO</li><li><a href="https://github.com/ifew/aws-lambda-netcore3-readytorun-db">aws-lambda-netcore3-readytorun-db</a> &#8211; ทดสอบทำ Lambda ด้วย .NET Core 3 ReadyToRun กับ ADO</li><li><a href="https://github.com/ifew/aws-lambda-netcore3-readytorun-efcore">aws-lambda-netcore3-readytorun-efcore</a> &#8211; ทดสอบทำ Lambda ด้วย .NET Core 3 ReadyToRun กับ EFCore</li><li><a href="https://github.com/ifew/aws-lambda-netcore3-readytorun-dapper">aws-lambda-netcore3-readytorun-dapper</a> &#8211; ทดสอบทำ Lambda ด้วย .NET Core 3 ReadyToRun กับ Dapper</li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/5565/benchmark-netcore-3-and-readytorun-for-aws-lambda/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ทดสอบ AWS Lambda Function บนเครื่องตัวเอง ด้วย Lambda Docker</title>
		<link>https://myifew.com/5257/how-to-test-aws-lambda-on-local-machine-via-lambda-docker/</link>
					<comments>https://myifew.com/5257/how-to-test-aws-lambda-on-local-machine-via-lambda-docker/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Thu, 21 Feb 2019 16:25:07 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWS Lambda]]></category>
		<category><![CDATA[Serverless]]></category>
		<category><![CDATA[Testing Tools]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=5257</guid>

					<description><![CDATA[จากที่เคยนำเสนอเครื่องมือตัวหนึ่งไปแล้วในบล็อก ทดสอบ .NET AWS Lambda ด้วย AWS .NET Mock Lambda Test Tool แต่ยังไม่หนำใจ เพราะผมพบว่ามันมีปัญหากับโค้ดที่เขียนแบบ Dependency Injection ซึ่งลองหาวิธี Debug&#8230;]]></description>
										<content:encoded><![CDATA[
<p>จากที่เคยนำเสนอเครื่องมือตัวหนึ่งไปแล้วในบล็อก <a href="https://myifew.com/5223/test-netcore-aws-lambda-by-aws-net-mock-lambda-test-tool/">ทดสอบ .NET AWS Lambda ด้วย AWS .NET Mock Lambda Test Tool</a> แต่ยังไม่หนำใจ เพราะผมพบว่ามันมีปัญหากับโค้ดที่เขียนแบบ Dependency Injection ซึ่งลองหาวิธี Debug แต่ก็ยังไม่เห็นอะไร เลยตัดใจลองหาเครื่องมือตัวอื่นดู ก็เจอ <a rel="noreferrer noopener" aria-label="AWS SAM (AWS Serverless Application Model) (opens in a new tab)" href="https://aws.amazon.com/serverless/sam/" target="_blank">AWS SAM (AWS Serverless Application Model)</a> </p>



<p>แต่ก็กระนั้นอีก ในหน้าเว็บโปรเจ็คบอกทำงานกับ .NET Core 2.1 ได้ แต่พอลองทำดู กลับบอกว่า ไม่รองรับ .NET Core Runtime!! (ใครแก้ปัญหาสองข้อบนของผมได้ มาแลกเปลี่ยนความรู้กันหน่อยครับ จักหายคาใจ) ก็เลยต้องแกะมัน พบว่า มันไปใช้ Lambda Docker เพื่อสร้าง Environment จำลองขึ้นมาสำหรับทดสอบ.. ปั๊ดโธ่ว แบบนี้ก็เสร็จโก๋!!  จึงเป็นที่มาของบล็อกนี้..</p>



<span id="more-5257"></span>



<p style="text-align:center" class="has-background has-very-light-gray-background-color"><strong>คำแนะนำ: ผู้อ่านบล็อกนี้ควรใช้งาน AWS Lambda และ Docker เบื้องต้นแล้ว,&nbsp;ตัวอย่างใช้เป็น&nbsp;.NET&nbsp;Core 2.1&nbsp;แต่สามารถนำวิธีใช้กับภาษาอื่นๆได้</strong></p>



<h2 class="wp-block-heading"> Lambda Docker คืออะไร? ดีไหม?</h2>



<p><g class="gr_ gr_24 gr-alert gr_gramm gr_inline_cards gr_disable_anim_appear Grammar only-ins doubleReplace replaceWithoutSep" id="24" data-gr-id="24">เป็น</g>&nbsp;Docker ที่จำลอง Environment ไว้เพื่อทดสอบ (Sandbox) ของ&nbsp;<a href="https://aws.amazon.com/lambda/">AWS Lambda</a>&nbsp;ซึ่งเคลมตัวเองว่า ใกล้เคียงกับของจริงมากๆ</p>



<p>ถามว่าใกล้เคียงขนาดไหน? คำตอบคือใกล้มากจริงๆ ตามที่เขาโม้ไว้ เพราะภายใน image ของมันประกอบไปด้วย software, libraries, โครงสร้างของไฟล์ และ permissions, environment variables, context objects หรือแม้แต่  user สำหรับใช้เรียก process ก็ยังใช้ชื่อเดียวกัน</p>



<p>ด้วยความเหมือนเป๊ะ มันจึงเหมาะที่จะเอา Lambda Function มาทดสอบบน Docker Environment ที่มีข้อจำกัดคล้ายกับ AWS Lambda ได้เกือบ 100%, และที่สำคัญ มันปรับ Memory, Timeout และใช้ Layer ได้ด้วยนะ </p>



<p>มีข้อดีแล้วก็ต้องมีข้อเสีย ซึ่งตอนนี้ผมพบจุดเดียว คือ เรื่องของเวลาในการคำนวณ ซึ่งเอาไปอ้างอิงไม่ได้ เพราะไม่มี Cold Start และมันเป็น CPU บนเครื่องเรา, ถ้าจำทดสอบเพื่อไปคำนวณค่าใช้จ่าย ควรใช้เครื่องมืออีกตัวชื่อว่า <a rel="noreferrer noopener" aria-label="AWS Lambda Power Tuning (opens in a new tab)" href="https://github.com/alexcasalboni/aws-lambda-power-tuning" target="_blank">AWS Lambda Power Tuning</a> (ไว้จะเขียนถึงอีกที)</p>



<h2 class="wp-block-heading">สิ่งที่ต้องติดตั้งก่อนใช้ Lambda Docker </h2>



<p>ก่อนไปต่อ ควรติดตั้ง <a rel="noreferrer noopener" aria-label="Docker (opens in a new tab)" href="https://www.docker.com/get-started" target="_blank">Docker</a> ซะก่อนนะ (เช็คให้เรียบร้อยว่าไม่ติด Security Policy ทำงานได้ดี สามารถ Pull Image และ Run Container ได้)</p>



<h2 class="wp-block-heading">เริ่มต้นใช้งาน Lambda Docker</h2>



<p>รูปแบบของคำสั่งจะเป็นดังนี้</p>



<pre class="wp-block-code"><code>docker run [--rm] -v &lt;code_dir>:/var/task [-v &lt;layer_dir>:/opt] lambci/lambda:&lt;runtime> [&lt;handler>] [&lt;event>]</code></pre>



<p>การทำงานของมันคือ จะสร้าง Docker Container ขึ้นมา และเอาโค้ดของเราไปรันทดสอบ ด้วยตัวแปรต่างๆที่เราระบุไว้ใน Command แค่นั้นเอง</p>



<ul class="wp-block-list"><li><strong>&#8211;rm</strong> คือเพื่อให้สร้าง Container มาทดสอบเพียงครั้งเดียว และทำลายตัวเองทิ้ง (ไม่ใส่ก็ได้ รันทำงานซ้ำไปได้เรื่อยๆ)</li><li><strong> -v &lt;code_dir&gt;:/var/task</strong> คือ ชี้ที่อยู่ของโค้ดเรา ไปให้ตรงกับโฟลเดอร์ใน Container เพื่อใช้งาน</li><li> <strong>-v &lt;layer_dir&gt;:/opt</strong> คือ ชี้ที่อยู่ของ layer  โค้ด ไปให้ตรงกับโฟลเดอร์ใน Container เพื่อใช้งาน (ถ้าไม่รู้จัก หรือไม่ได้ใช้ ก็ไม่ต้องใส่)</li><li><strong>&lt;runtime&gt;</strong> &nbsp;คือ Runtime Language ที่เราใช้เขียน Function เช่น dotnetcore2.1, nodejs4.3, python3.7, java8, go1.x</li><li><strong>&lt;handler&gt;</strong> คือ Fucntion Handler </li><li><strong>&lt;event&gt;</strong> คือ Request Input นั่นหละ</li></ul>



<h2 class="wp-block-heading">ทดสอบรันคำสั่งง่ายๆ</h2>



<p>ตัวอย่าง Function ของผมชื่อ aws_lambda_function โดยรับ string เข้าไป จากนั้นจะพ่นออกมาว่า &#8220;Hello, &lt;ชื่อ&gt;&#8221; โดยมีรูปแบบคำสั่งคือ</p>



<pre class="wp-block-code"><code>docker run --rm -v "$PWD":/var/task lambci/lambda:dotnetcore2.1 aws-lambda-function::aws_lambda_function.Function::FunctionHandler '"iFew"'</code></pre>



<p>เนื่องด้วยการทำงานมันจะ run code ที่ผ่านการ complie แล้ว, ซึ่งถ้าให้แน่ใจ ว่าไม่ได้รันอยู่บนโค้ดชุดเดิม ให้เราทำคำสั่ง compile ก่อนสักครั้ง จากนั้นไปที่โฟลเดอร์โค้ดที่ Compiled และทำคำสั่งด้านบนอีกที (ผมชี้ที่อยู่โค้ดไปที่ $PWD หมายถึง ให้ใช้โค้ดในโฟลเดอร์ปัจจุบันที่ผมอยู่)</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="132" src="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-1-1200x132.png" alt="" class="wp-image-5266" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-1-1200x132.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-1-1024x112.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-1-768x84.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-1-700x77.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-1-600x66.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-1.png 2104w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>ผลลัพธ์ที่ได้</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="93" src="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-result-1-1200x93.png" alt="" class="wp-image-5267" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-result-1-1200x93.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-result-1-1024x79.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-result-1-768x59.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-result-1-700x54.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-result-1-600x46.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-command-result-1.png 2402w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h2 class="wp-block-heading" id="mce_51">ทดสอบรันคำสั่งโดยใส่ Environment Variable</h2>



<p>จากตัวอย่างแรก สังเกตว่ามัน Allocate Memory ให้ถึง 1,536MB เลยทีเดียว (เป็น Default ของมัน) ซึ่งผมอยากกำหนดให้ใช้ Memory แค่ 128MB และ Timeout 30 วินาที เพื่อทดสอบการทำงาน ในสภาพแวดใกล้เคียงของจริง ด้วยคำสั่งนี้</p>



<pre class="wp-block-code"><code>docker run --rm -v "$PWD":/var/task -e AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128 -e AWS_LAMBDA_FUNCTION_TIMEOUT=30 lambci/lambda:dotnetcore2.1  aws-lambda-function::aws_lambda_function.Function::FunctionHandler '"iFew"'</code></pre>



<p>ซึ่ง ใช้ parameter ชื่อ -e ของ Docker นั่นหละเป็นตัวกำหนด โดยลัพลัพธ์ที่ได้ คือ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="95" src="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-env-command-result-1200x95.png" alt="" class="wp-image-5269" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-env-command-result-1200x95.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-env-command-result-1024x81.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-env-command-result-768x61.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-env-command-result-700x55.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-env-command-result-600x47.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-env-command-result.png 2684w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>สังเกตว่า Memory ตอนนี้จัดสรรให้แค่ 128MB เท่านั้น ส่วน Timoue ยังไม่เห็นผลในตอนนี้เพราะโค้ดง่ายๆ ทำงานไว</p>



<p>ตัว environment ให้ใช้งานต่างๆ ดูเพิ่มเติมได้ที่ https://github.com/lambci/docker-lambda</p>



<h2 class="wp-block-heading">ทดสอบรันคำสั่งโดยอ้างอิงกับ Database ภายนอก</h2>



<p>การใช้งานจริงๆของเรา มันไม่ง่ายเหมือน Function ด้านบนแน่นอน ซึ่งผมเจอปัญหานี้เช่นกัน และใน Document ไม่ได้บอกไว้ว่าทำอย่างไร งมอยู่สักพัก ถึงเจอวิธี โดยการใช้ทำให้ Lambda Docker ของเราอยู่ในวง Network เดียวกับ Docker Database ที่ผมมีซะ (พอดีผมใช้ MySQL บน Docker, ถ้าใครใช้ MySQL เข้าใจว่าสามารถใช้งานได้เลย เนื่องจาก network เป็นแบบ bridge ที่ทำให้ Container เราต่ออินเทอร์เน็ตหรือวง Network ในเครื่องเราได้อยู่แล้ว)</p>



<p>ลองดูตัวอย่างคำสั่งนี้ครับ</p>



<pre class="wp-block-code"><code>docker run --rm -v "$PWD":/var/task --network mysql -e TEST_LAMBDA_DBCONNECTION="server=mysql;userid=root;password=1234;database=test_lambda;convert zero datetime=True; CharSet=utf8" -e AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128 lambci/lambda:dotnetcore2.1 list_profile::list_profile.Function::Get</code></pre>



<p>จะสังเกตว่ามีตัวแปรแปลกๆมานิดนึง คือ &#8211;network mysql หมายถึง ให้ Container นี้อยู่ในวง Network ชื่อ mysql โดยในวงนั้นผมมี Container MySQL อยู่ในนั้นอยู่แล้ว</p>



<p>จากนั้นผมระบุ Environment Variable ชื่อว่า TEST_LAMBDA_DBCONNECTION เข้าไป เนื่องจากโค้ดของผมเรียกใช้ config ผ่าน Environment Variable (ตาม<a rel="noreferrer noopener" aria-label="หลักการข้อ 3 ใน The Twelve Factor (opens in a new tab)" href="https://12factor.net/config" target="_blank">หลักการข้อ 3. Config ใน The Twelve Factor</a>)</p>



<p>ผลลัพธ์ที่ได้คือ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="117" src="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-mysql-command-result-1200x117.png" alt="" class="wp-image-5270" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-mysql-command-result-1200x117.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-mysql-command-result-1024x100.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-mysql-command-result-768x75.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-mysql-command-result-700x68.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-mysql-command-result-600x59.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-basic-mysql-command-result.png 2680w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h4 class="wp-block-heading">แถมนิดนึง วิธีการสร้าง Docker Network</h4>



<p>คำสั่งง่ายๆ คือ</p>



<pre class="wp-block-code"><code>docket create network &lt;ชื่อnetworkที่ต้องการ></code></pre>



<p>แค่นี้ก็จะได้วง Network มาแล้วครับ โดยในตัวอย่างผมใช้ชื่อวงว่า mysql</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="976" height="226" src="https://myifew.com/wp-content/uploads/2019/02/lambda-docket-network-2.png" alt="" class="wp-image-5274" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-docket-network-2.png 976w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-network-2-768x178.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-network-2-700x162.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-network-2-600x139.png 600w" sizes="auto, (max-width: 976px) 100vw, 976px" /></figure>



<p>ดังนั้น หากเรามี Container อยู่แล้ว ให้เพิ่มเข้าวง Network ด้วยคำสั่ง</p>



<pre class="wp-block-code"><code>docker network connect &lt;ชื่อnetwork> &lt;ชื่อcontainer></code></pre>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="98" src="https://myifew.com/wp-content/uploads/2019/02/lambda-docket-create-network-1200x98.png" alt="" class="wp-image-5273" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-docket-create-network-1200x98.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-create-network-1024x83.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-create-network-768x63.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-create-network-700x57.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-create-network-600x49.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-docket-create-network.png 2134w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h2 class="wp-block-heading" id="mce_69">ทดสอบรันคำสั่งโดยส่ง Request Input ด้วย APIGatewayProxy (หรือรันคำสั่งแบบมี JSON  Input ยาวๆ)</h2>



<p>โดยปกติผมจะใช้ AWS API Gateway อยู่แล้ว ดังนั้นตัว Request Input ผมจะชอบใช้ในรูปแบบของ API Gateway Proxy Request เพราะมันสามารถส่ง Request Input ได้ทั้งแบบ Path Parameter, Query String, Header, Body ซึ่งลดควมซับซ้อน ไม่ต้องไปทำการ Mapping Input Data อะไรให้ยุ่งยากใน AWS API Gateway ซึ่งถ้าคุณใช้เหมือนกัน ก็สามารถเอาตัวอย่างนี้ไปทดสอบกับ Lambda Docker ได้ครับ</p>



<p>โดยตัว API Gateway Proxy Request เนื้อมันจริงๆคือ JSON ที่ AWS API Gateway รับเข้าไป จากนั้นมันจะกระทำการ Mapping นั่นโน่นนี่ให้เอง และสร้าง Request ตามที่เรากำหนดใน JSON นั้นไปทำงานกับ Function เราอีกที ซึ่งรูปแบบของ JSON มันจะยาวมากๆ ครับ ดังนั้น การส่ง Input ยาวๆ เราสามารถทำได้สามแบบ</p>



<h4 class="wp-block-heading">ในตัวอย่างแรก สามารถใส่ event ต่อท้ายได้ตามรูปแบบมาตรฐานของมันเลยครับ</h4>



<pre class="wp-block-code"><code>docker run --rm -v "$PWD":/var/task --network mysql -e TEST_LAMBDA_DBCONNECTION="server=mysql;userid=root;password=1234;database=test_lambda;convert zero datetime=True; CharSet=utf8" -e AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128 lambci/lambda:dotnetcore2.1 get_profile::get_profile.Function::Get '{"resource":"/{proxy+}","path":"/","httpMethod":"GET","headers":null,"queryStringParameters":null,"pathParameters":{"id":"1"},"stageVariables":null,"requestContext":{"accountId":"AAAAAAAAAAAA","resourceId":"5agfss","stage":"test-invoke-stage","requestId":"test-invoke-request","identity":{"cognitoIdentityPoolId":null,"accountId":"AAAAAAAAAAAA","cognitoIdentityId":null,"caller":"BBBBBBBBBBBB","apiKey":"test-invoke-api-key","sourceIp":"test-invoke-source-ip","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":"arn:aws:iam::AAAAAAAAAAAA:root","userAgent":"Apache-HttpClient/4.5.x (Java/1.8.0_102)","user":"AAAAAAAAAAAA"},"resourcePath":"/{proxy+}","httpMethod":"GET","apiId":"t2yh6sjnmk"},"body":null}'</code></pre>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="138" src="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-apiproxy-mysql-command-result1-1200x138.png" alt="" class="wp-image-5275" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-docker-apiproxy-mysql-command-result1-1200x138.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-apiproxy-mysql-command-result1-1024x118.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-apiproxy-mysql-command-result1-768x89.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-apiproxy-mysql-command-result1-700x81.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-apiproxy-mysql-command-result1-600x69.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-docker-apiproxy-mysql-command-result1.png 2690w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h4 class="wp-block-heading">ตัวอย่างสอง สามารถใส่ event ด้านหน้า ในรูปแบบ stdin ได้</h4>



<p>โดยใช้การ echo ค่า JSON ที่เราต้องการส่งออกไป จากนั้นให้ ใส่ | คั่นคำสั่งรัน Container เพื่อรับไปใช้ พร้อมกับระบุ parameter เพิ่มเติมอีกหน่อยคือ</p>



<pre class="wp-block-code"><code>-i -e DOCKER_LAMBDA_USE_STDIN=1</code></pre>



<p>ตัวอย่าง</p>



<pre class="wp-block-code"><code>echo '{"resource":"/{proxy+}","path":"/","httpMethod":"GET","headers":null,"queryStringParameters":null,"pathParameters":{"id":"1"},"stageVariables":null,"requestContext":{"accountId":"AAAAAAAAAAAA","resourceId":"5agfss","stage":"test-invoke-stage","requestId":"test-invoke-request","identity":{"cognitoIdentityPoolId":null,"accountId":"AAAAAAAAAAAA","cognitoIdentityId":null,"caller":"BBBBBBBBBBBB","apiKey":"test-invoke-api-key","sourceIp":"test-invoke-source-ip","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":"arn:aws:iam::AAAAAAAAAAAA:root","userAgent":"Apache-HttpClient/4.5.x (Java/1.8.0_102)","user":"AAAAAAAAAAAA"},"resourcePath":"/{proxy+}","httpMethod":"GET","apiId":"t2yh6sjnmk"},"body":null}' | docker run --rm -v "$PWD":/var/task -i -e DOCKER_LAMBDA_USE_STDIN=1 --network mysql -e TEST_LAMBDA_DBCONNECTION="server=mysql;userid=root;password=1234;database=test_lambda;convert zero datetime=True; CharSet=utf8" -e AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128 lambci/lambda:dotnetcore2.1 get_profile::get_profile.Function::Get</code></pre>



<h4 class="wp-block-heading" id="mce_97">ตัวอย่างสาม สามารถใส่ event ด้านหน้า ในรูปแบบ stdin ได้ โดยเรียกใช้ข้อมูลจากไฟล์</h4>



<p>แบบนี้เหมือนกับด้านบน เพียงแต่ผมแปลงนิดหน่อย ให้พ่นข้อมูลจากไฟล์แทน โดยใช้คำสั่ง cat เช่น</p>



<pre class="wp-block-code"><code>cat /Users/chitpong/Sourcecode/aws-serverless/profile/test/get_profile.Tests/SampleRequests/TestGetMethod.json | docker run --rm -v "$PWD":/var/task -i -e DOCKER_LAMBDA_USE_STDIN=1 --network mysql -e TEST_LAMBDA_DBCONNECTION="server=mysql;userid=root;password=1234;database=test_lambda;convert zero datetime=True; CharSet=utf8" -e AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128 lambci/lambda:dotnetcore2.1 get_profile::get_profile.Function::Get</code></pre>



<p>ในที่นี้ผมใช้ JSON ไฟล์เดียวกันกับที่ผมเขียนทดสอบใน Unit Test</p>



<h2 class="wp-block-heading">สรุป</h2>



<p>เป็นรุปแบบการทดสอบ AWS Lambda แบบที่ไม่เสียเงินอีกรูปแบบหนึ่ง และค่อนข้างทำงานได้เหมือนจริงตามที่เราต้องการ ซึ่งถ้าใช้งานบ่อยๆ การทำคำสั่งแบบนี้อาจจะยาวและเสียเวลาหน่อย ก็ให้ไปเขียนเป็น shell script หรือ makefile เพื่อรันก็จะไวขึ้นอีกนิด</p>



<p>ทั้งนี้ทั้งนั้น ผมก็ยังยืนยันแบบเดิมว่า ทดสอบด้วยการเขียน Unit Test เถอะครับ</p>



<p><strong>Reference</strong></p>



<ul class="wp-block-list"><li>https://github.com/lambci/docker-lambda</li><li>https://github.com/lambci/docker-lambda/issues/23</li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/5257/how-to-test-aws-lambda-on-local-machine-via-lambda-docker/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>4 วิธีจัดการ Versioning และ Environment ของ AWS Lambda Function โดยไม่ต้องแก้ไขโค้ด</title>
		<link>https://myifew.com/5231/4-ways-to-manage-aws-lambda-function-version-and-environment/</link>
					<comments>https://myifew.com/5231/4-ways-to-manage-aws-lambda-function-version-and-environment/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Wed, 20 Feb 2019 04:23:29 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWS Lambda]]></category>
		<category><![CDATA[Serverless]]></category>
		<category><![CDATA[Versioning]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=5231</guid>

					<description><![CDATA[เป็นเรื่องสำคัญที่เรามักมองข้ามไป ว่าจะจัดการ Lambda Function อย่างไรให้ใช้ได้กับ Environment ต่างๆ เช่น Development, UAT, Production และต้องไม่กระทบกับการใช้งานของผู้ใช้ด้วย ดังนั้น สิ่งที่ต้องดูต่อ คือ เราต้องกำหนด Lambda Function&#8230;]]></description>
										<content:encoded><![CDATA[
<p>เป็นเรื่องสำคัญที่เรามักมองข้ามไป ว่าจะจัดการ Lambda Function อย่างไรให้ใช้ได้กับ Environment ต่างๆ เช่น Development, UAT, Production และต้องไม่กระทบกับการใช้งานของผู้ใช้ด้วย ดังนั้น สิ่งที่ต้องดูต่อ คือ เราต้องกำหนด Lambda Function Version ให้แต่ละ Environment ด้วยใช่หรือไม่?</p>



<p>จากที่ผมลองเล่นดู พบอยู่ 4 วิธี ที่กำลังจะเขียนแชร์ในบล็อกนี้เอง มันมีทั้งข้อดีและข้อเสีย ซึ่งผู้อ่านคงต้องนำไปพิจารณาและตัดสินใจใช้ตามความเหมาะสมกันเอง</p>



<span id="more-5231"></span>



<p class="has-background has-very-light-gray-background-color"><strong>คำแนะนำ: วิธีการที่ผมเสนอในบล็อกนี้ จะไม่ได้พูดถึงวิธีการที่ต้องมีการแก้โค้ดนะครับ รวมไปถึง ผู้อ่านควรต้องเข้าใจเรื่อง AWS API Gateway และ AWS Lambda อยู่แล้ว</strong></p>



<h2 class="wp-block-heading">1. ปรับ Version ที่ Integration Request ใน API Gateway </h2>



<p><strong>วิธีนี้เหมาะสำหรับ</strong></p>



<ul class="wp-block-list"><li>ผู้ใช้ API Gateway เป็น Event Trigger เรียกใช้งาน Lambda Function</li><li>มี Lambda Function จำนวนน้อย</li><li>ทำไว้ใช้บน Test Environment เท่านั้น</li><li>ไม่ได้ต้องการทำ Version ให้กับ Lambda Function ทุกตัว </li><li>ถ้าใช้ AWS API Gateway วิธีการนี้ จะสามารถใช้งานสิ่งที่เราปรับเปลี่ยนได้ก็ต่อเมื่อมีการกดปุ่ม Deploy API</li></ul>



<p><strong>ปัญหาที่พบในวิธีนี้</strong></p>



<ul class="wp-block-list"><li>ถ้าไม่ใช้ API Gateway จะทำแบบนี้ไม่ได้</li><li>ถ้ามี Lambda Function จำนวนมากๆ ต้องไล่เปลี่ยนทีละตัว ก็จะเป็นงานหนักมาก</li></ul>



<p>วิธีนี้เป็นวิธี basic ที่สุดสำหรับ ผู้ใช้ API Gateway เป็น Event Trigger เรียกใช้งาน Lambda Function ซึ่งตอนผมลองเล่นแรกๆก็ทำแบบนี้แหละ</p>



<p>ในตัวอย่าง บนหน้าของ AWS API Gateway เมื่อเราสร้าง Resource และ Method แล้ว จะมีส่วนของ Integration Request</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="565" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-1-1200x565.png" alt="" class="wp-image-5234" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-1-1200x565.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-1-1024x482.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-1-768x362.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-1-700x330.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-1-600x283.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-1.png 2870w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>ในหน้าของ Integration Request เราสามารถแก้ไขการเรียกใช้งาน Lambda Function โดยระบุ Version ได้จากที่นี่เลย</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="797" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-2-1200x797.png" alt="" class="wp-image-5235" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-2-1200x797.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-2-1024x680.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-2-768x510.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-2-700x465.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-2-600x398.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-2.png 1684w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="294" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-3-1200x294.png" alt="" class="wp-image-5236" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-3-1200x294.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-3-1024x251.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-3-768x188.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-3-700x171.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-3-600x147.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-pointer-3.png 1796w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h2 class="wp-block-heading">2. สร้าง Version URL ใน API Gateway</h2>



<p><strong>วิธีนี้เหมาะสำหรับ</strong></p>



<ul class="wp-block-list"><li>ผู้ใช้ API Gateway เป็น Event Trigger เรียกใช้งาน Lambda Function</li><li>ต้องการให้ผู้ใช้ API เดิมยังใช้งานได้ตามปกติ และผู้ใช้ใหม่ก็สามารถใช้ API ใหม่ได้ (หรือจะกำหนด Permission ของกลุ่มผู้ใช้เพิ่มด้วยก็ได้)</li><li>มี Lambda Function จำนวนไม่มากนัก</li><li>สามารถ Custom Lambda ในแต่ละ URL Version ได้ โดยไม่กระทบกับ URL Version อื่น</li><li>ถ้าใช้ AWS API Gateway วิธีการนี้ จะสามารถใช้งานสิ่งที่เราปรับเปลี่ยนได้ก็ต่อเมื่อมีการกดปุ่ม Deploy API</li></ul>



<p><strong>ปัญหาที่พบในวิธีนี้</strong></p>



<ul class="wp-block-list"><li>ถ้าไม่ใช้ API Gateway หรือ Middleware ที่สามารถจัดการ Router ได้ จะทำแบบนี้ไม่ได้</li><li>จะงานหนักทันที ถ้ามี Lambda Function จำนวนมากๆ และต้องสร้าง URL Gateway ใหม่อีกชุดหนึ่ง และกำหนด Version ใหม่ (แต่ใช้ Tools จำพวก <a rel="noreferrer noopener" aria-label="Infrastructure as code (opens in a new tab)" href="https://en.wikipedia.org/wiki/Infrastructure_as_code" target="_blank">Infrastructure as code</a> ช่วยปั๊มได้)</li></ul>



<p>วิธีนี้เพิ่มมิติของการทำ Versioning โดยการกำหนด URL Version ไปด้วย ทำให้แยกกลุ่มของผู้ใช้งานได้ โดยไม่กระทบกัน</p>



<p>มีหลายแบรนด์ดังที่ใช้วิธีการแบบนี้ เช่น</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="981" src="https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-20-at-12.25.05-1200x981.png" alt="" class="wp-image-5237" srcset="https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-20-at-12.25.05-1200x981.png 1200w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-20-at-12.25.05-1024x837.png 1024w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-20-at-12.25.05-768x628.png 768w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-20-at-12.25.05-700x572.png 700w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-20-at-12.25.05-600x491.png 600w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-20-at-12.25.05.png 1416w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>อ้างอิง: https://mathieu.fenniak.net/aint-nobody-got-time-for-that-api-versioning/</figcaption></figure>



<p>ในตัวอย่าง บนหน้าของ AWS API Gateway คือให้เราสร้าง Resource เป็น Version ไว้เลย</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="566" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-1-1200x566.png" alt="" class="wp-image-5238" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-1-1200x566.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-1-1024x483.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-1-768x362.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-1-700x330.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-1-600x283.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-1.png 2874w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>จากนั้นในแต่ละ Method ใน Resource Version ต่างๆ ให้เรากำหนด Version ใน Integration Request เหมือนเดิมแบบวิธีแรก</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="565" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-2-1200x565.png" alt="" class="wp-image-5239" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-2-1200x565.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-2-1024x482.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-2-768x362.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-2-700x330.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-2-600x282.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-gw-url-2.png 2872w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h2 class="wp-block-heading">3. สร้าง Version และกำหนด Alias ให้กับ Lambda Function  </h2>



<p><strong>วิธีนี้เหมาะสำหรับ</strong></p>



<ul class="wp-block-list"><li>ทุกคนที่จะนำ Lambda Function ไปใช้งาน</li><li>มี Lambda Function จำนวนน้อยจนถึงมากๆ </li><li>ใช้ได้กับทุก Environment ไม่ว่าจะ Test หรือ Production</li><li>มีการกำหนด Version ให้กับ Lambda Function อยู่เสมอ</li></ul>



<p><strong>ปัญหาที่พบในวิธีนี้</strong></p>



<ul class="wp-block-list"><li>ต้องระบุ Description ใน Version ให้ดี เพื่อที่จะได้รู้ว่า แต่ละตัว มีการปรับเปลี่ยนอะไร</li><li>การเปลี่ยน Verion ใน Alias Name จะทำให้การทำงานไปใช้ Lambda Version นั้นทันที ไม่หมือนกับตอนทำกับ API Gateway ที่ทดสอบก่อนได้ แล้วค่อยกดปุ่ม Deploy API (แต่ทั้งนีก็สามารถ Rollback ได้ทันที)</li></ul>



<p>เป็นวิธีที่ซับซ้อนขึ้นอีกนิดจากข้างต้น แต่มีประสิทธิภาพ และทาง Amazon เองก็แนะนำให้ทำแบบนี้ (<a rel="noreferrer noopener" aria-label="อ้างอิง (opens in a new tab)" href="https://docs.aws.amazon.com/lambda/latest/dg/versioning-aliases.html" target="_blank">AWS Lambda Function Versioning and Aliases</a>)</p>



<p>โปรแกรมเมอร์เองจะต้องมีการกำหนดเวอร์ชั่นภายในทีมให้ชัดเจนว่า เมื่อไรถึงจะต้อง publish version ออกมา และนำไปลิงค์เข้าใน Alias Name เพราะการกำหนดแบบนี้จะทำให้การทำงานของ lambda เปลี่ยนทันที ไม่มีการผ่าน Deploy API</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="377" height="360" src="https://myifew.com/wp-content/uploads/2019/02/alias_intro_2_10.png" alt="" class="wp-image-5249"/><figcaption>ตัวอย่างมี 2 Environment และเมื่อทำบน Development Env. เสร็จ ต้องการนำไปใช้บน Prodution Env.<br>อ้างอิง https://docs.aws.amazon.com/lambda/latest/dg/versioning-aliases.html</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="564" height="363" src="https://myifew.com/wp-content/uploads/2019/02/alias_scenario_2_30.png" alt="" class="wp-image-5252"/><figcaption>ตัวอย่างมี 3 Environment และปรับให้มีการใช้ Lambda ร่วมกัน<br>อ้างอิง https://docs.aws.amazon.com/lambda/latest/dg/aliases-intro.html</figcaption></figure></div>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="569" height="358" src="https://myifew.com/wp-content/uploads/2019/02/alias_scenario_2_40.png" alt="" class="wp-image-5253"/><figcaption>ตัวอย่างมี 3 Environment และมีการใช้ Lambda Version ของใครของมัน (การทำงานจริง น่าจะเป็นภาพนี้)<br>อ้างอิง https://docs.aws.amazon.com/lambda/latest/dg/aliases-intro.html</figcaption></figure>



<p>วิธีการทำ คือ เมื่อเรามี Version ของ Lambda อยู่แล้ว ให้ทำการสร้าง Alias Name และทำการเชื่อมกันว่า ชื่อ Alias ไหน ชี้ไป Version ไหน</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="908" height="726" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-4.png" alt="" class="wp-image-5242" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-4.png 908w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-4-768x614.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-4-700x560.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-4-600x480.png 600w" sizes="auto, (max-width: 908px) 100vw, 908px" /></figure>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="892" height="768" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-5.png" alt="" class="wp-image-5243" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-5.png 892w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-5-768x661.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-5-700x603.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-5-600x517.png 600w" sizes="auto, (max-width: 892px) 100vw, 892px" /></figure>



<p>ถ้าต้องการเปลี่ยน Version ก็สามารถคลิกที่ชื่อ Alias แล้วเลื่อนลงมาแก้ไข Config ได้ภายหลัง, รูปนี้ตัวอย่างของ Alias ชื่อ Prod ชี้ไปหาฟังก์ชั่น simple-hello Version 1</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="777" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-6-1200x777.png" alt="" class="wp-image-5244" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-6-1200x777.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-6-1024x663.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-6-768x498.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-6-700x453.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-6-600x389.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-6.png 1908w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>รูปนี้ตัวอย่างของ Alias ชื่อ Dev ชี้ไปหาฟังก์ชั่น simple-hello Version $LATEST</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="817" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-7-1200x817.png" alt="" class="wp-image-5245" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-7-1200x817.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-7-1024x697.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-7-768x523.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-7-700x476.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-7-600x408.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-7.png 1860w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>ในตัวอย่าง บนหน้าของ AWS API Gateway คือให้เราสร้าง Resource และกำหนดให้ Integration Request ของ Method ชี้ไปหา Alias ชื่อว่า Prod (แต่เดิมเราชี้ไปหาเลข Version, ตอนนี้ชี้ไปหาชื่อ Alias แทน) จากนั้นผม Deploy API ไปหา prod stage รอบหนึ่งก่อน เพื่อให้จำค่านั้น แล้วผมก็กลับมาเปลี่ยน Method ให้ชี้ไปหา Alias ชื่อว่า Dev และทำการ Deploy API ไปหา dev stage  อีกครั้ง</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="567" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-1-1200x567.png" alt="" class="wp-image-5241" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-1-1200x567.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-1-1024x484.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-1-768x363.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-1-700x331.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-1-600x283.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-1.png 2870w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>ผลที่ได้จากการทำวิธีนี้</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="212" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-2-1200x212.png" alt="" class="wp-image-5247" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-2-1200x212.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-2-1024x181.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-2-768x136.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-2-700x124.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-2-600x106.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-2.png 2180w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="194" src="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-3-1-1200x194.png" alt="" class="wp-image-5248" srcset="https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-3-1-1200x194.png 1200w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-3-1-1024x166.png 1024w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-3-1-768x124.png 768w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-3-1-700x113.png 700w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-3-1-600x97.png 600w, https://myifew.com/wp-content/uploads/2019/02/lambda-version-by-lambda-alias-3-1.png 2176w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h2 class="wp-block-heading" id="mce_42">4. สร้าง Version และกำหนด Alias ให้กับ Lambda Function พร้อมกับทำ URL Verioning</h2>



<p>วิธีนี้คือการผสมข้อ 3 และ 4 เข้าด้วยกัน กล่าวคือ ใช้ Alias Name ด้วย เพื่อให้แก้ไข Version ที่ตัว Lambda Function ได้เองเลย พร้อมกับทำ URL กำหนด Version ไว้ เพื่อป้องกันผลกระทบ กรณี เปลี่ยน Version ใน Lambda Function ผิด</p>



<h2 class="wp-block-heading">สรุป</h2>



<p>วิธีการระบุ API Version มีหลายวิธีการให้เลือกใช้ พยายามหา Best Practice ก็อาจจะเจอหลายๆแบบ แล้วแต่ว่าคนเขียนเคยทำแบบไหนแล้วเหมาะกับงานของเขา</p>



<h2 class="wp-block-heading">แถม</h2>



<p>อย่างที่ผมกล่าวข้างต้น ในบล็อกนี้มันเป็นวิธีทำที่เราไม่ต้องไปแก้ไขโค้ดใดๆ  ส่วนวิธีการอย่างอื่น ก็มีอีกหลายแบบ ซึ่งลองดูรูปด้านล่างประกอบได้ครับ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="893" height="305" src="https://myifew.com/wp-content/uploads/2019/02/API-Versioning-Practices.png" alt="" class="wp-image-5251" srcset="https://myifew.com/wp-content/uploads/2019/02/API-Versioning-Practices.png 893w, https://myifew.com/wp-content/uploads/2019/02/API-Versioning-Practices-768x262.png 768w, https://myifew.com/wp-content/uploads/2019/02/API-Versioning-Practices-700x239.png 700w, https://myifew.com/wp-content/uploads/2019/02/API-Versioning-Practices-600x205.png 600w" sizes="auto, (max-width: 893px) 100vw, 893px" /></figure>



<p>ส่วนข้อดีข้อเสีย มีเทียบไว้ให้เข้าใจง่ายๆ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="712" height="217" src="https://myifew.com/wp-content/uploads/2019/02/API_1.png" alt="" class="wp-image-5255" srcset="https://myifew.com/wp-content/uploads/2019/02/API_1.png 712w, https://myifew.com/wp-content/uploads/2019/02/API_1-700x213.png 700w, https://myifew.com/wp-content/uploads/2019/02/API_1-600x183.png 600w" sizes="auto, (max-width: 712px) 100vw, 712px" /><figcaption>อ้างอิง https://www.icapps.com/blog/api-versioning-overview</figcaption></figure>



<p>อ้างอิง</p>



<ul class="wp-block-list"><li>https://docs.aws.amazon.com/lambda/latest/dg/versioning-aliases.html</li><li>https://docs.aws.amazon.com/lambda/latest/dg/aliases-intro.html</li><li>https://mathieu.fenniak.net/aint-nobody-got-time-for-that-api-versioning/</li><li>https://www.icapps.com/blog/api-versioning-overview</li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/5231/4-ways-to-manage-aws-lambda-function-version-and-environment/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ทดสอบ .NET AWS Lambda ด้วย  AWS .NET Mock Lambda Test Tool</title>
		<link>https://myifew.com/5223/test-netcore-aws-lambda-by-aws-net-mock-lambda-test-tool/</link>
					<comments>https://myifew.com/5223/test-netcore-aws-lambda-by-aws-net-mock-lambda-test-tool/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Mon, 18 Feb 2019 13:04:25 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWS Lambda]]></category>
		<category><![CDATA[Serverless]]></category>
		<category><![CDATA[Testing Tools]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=5223</guid>

					<description><![CDATA[ปัญหาหลักของของเขียน Serverless ต่างๆ คือจะทดสอบโค้ดตัวเองอย่างไร ไม่ให้เสียเงินค่า Compute และ Request ตลอดเวลา ยิ่งถ้าโค้ดนั้นไม่มี Unit Test แล้ว ยิ่งยาก เพราะเราไม่สามารถจำลอง Serverless Runtime ได้เอง&#8230;]]></description>
										<content:encoded><![CDATA[
<p>ปัญหาหลักของของเขียน Serverless ต่างๆ คือจะทดสอบโค้ดตัวเองอย่างไร ไม่ให้เสียเงินค่า Compute และ Request ตลอดเวลา ยิ่งถ้าโค้ดนั้นไม่มี Unit Test แล้ว ยิ่งยาก เพราะเราไม่สามารถจำลอง Serverless Runtime ได้เอง</p>



<p>ตัว AWS Lambda เอง เลยออกเครื่องมือตัวหนึ่งสำหรับชาว .Net ชื่อว่า AWS .NET Mock Lambda Test Tool เป็นเครื่องมือที่ช่วยให้นักพัฒนาสามารถทดสอบ Business Logic ของโค้ดตัวเองได้ง่ายๆ ไม่โดนคิดเงิน และคล้ายกับที่ทำงานบน AWS ด้วย</p>



<span id="more-5223"></span>



<h2 class="wp-block-heading">วิธีติดตั้ง</h2>



<p>ติดตั้งเครื่องมือด้วยคำสั่งนี้</p>



<pre class="wp-block-code"><code>dotnet tool install -g Amazon.Lambda.TestTool-2.1</code></pre>



<p>เสร็จเรียบร้อยครับ!</p>



<h2 class="wp-block-heading">วิธีการใช้งาน </h2>



<p>ให้เข้าไปที่โฟลเดอร์ของโค้ดเรา และอยู่ใน level เดียวกับที่มีไฟล์ .csproj จากนั้นรันคำสั่ง</p>



<pre class="wp-block-code"><code>dotnet publish</code></pre>



<p>เพื่อให้ build ไฟล์ DLL binary ก่อน จากนั้นรันคำสั่ง</p>



<pre class="wp-block-code"><code>dotnet lambda-test-tool-2.1</code></pre>



<p>เมื่อรันคำสั่งแล้วจะมีหน้าตาประมาณนี้</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="104" src="https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-command-1200x104.png" alt="" class="wp-image-5225" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-command-1200x104.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-command-1024x89.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-command-768x66.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-command-600x52.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-command.png 2036w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p> มันจะทำการ Mock Server ให้เรา จากนั้นมันจะเปิด Brower ขึ้นมา ให้เรากรอกข้อมูล Input เพื่อทำการทดสอบ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="1043" src="https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-dashboard-1200x1043.png" alt="" class="wp-image-5224" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-dashboard-1200x1043.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-dashboard-1024x890.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-dashboard-768x668.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-dashboard-600x522.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-dotnet-mock-lambda-test-tool-dashboard.png 1532w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h2 class="wp-block-heading">สรุป</h2>



<p>ใช้ง่ายไหมครับ ผมลองตัด Internet แล้วทดสอบ ก็ยังใช้ได้อยู่ แปลว่ามันไม่ได้วิ่งไปหา Amazon เพื่อทำให้เราเสียเงินในการทดสอบ, รวมถึงให้เรา Develop ได้ทุกที่แม้ไม่มีเน็ตด้วย</p>



<p>ส่วนใครคิดจะใช้ ขอให้เขาใจด้วยว่ามันมีข้อจำกัดบางอย่างอยู่ในตอนนี้</p>



<ul class="wp-block-list"><li>YAML based CloudFormation templates are not yet supported.</li><li>No mechanism for setting custom Environment variables.</li><li>NuGet packages that use native dependencies are not supported.</li></ul>



<p>ถ้าถามผม อย่างไรเสีย การมี Unit Test ก็ดีกว่านะครับ 😀</p>



<p><strong>Reference</strong></p>



<ul class="wp-block-list"><li>https://github.com/aws/aws-lambda-dotnet/tree/master/Tools/LambdaTestTool</li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/5223/test-netcore-aws-lambda-by-aws-net-mock-lambda-test-tool/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>วิธีง่ายๆ สำหรับทำ AWS Lambda Layers ที่เป็น C# .Net Core</title>
		<link>https://myifew.com/5189/working-with-aws-lambda-layers-and-csharp-net-core2/</link>
					<comments>https://myifew.com/5189/working-with-aws-lambda-layers-and-csharp-net-core2/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Fri, 08 Feb 2019 07:27:44 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWS Lambda]]></category>
		<category><![CDATA[Serverless]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=5189</guid>

					<description><![CDATA[มาลองดูวิธีลดขนาดไฟล์ AWS Lambda Function ของ C# .Net Core ด้วยการใช้ Layer]]></description>
										<content:encoded><![CDATA[
<p>ใน <a rel="noreferrer noopener" aria-label="Best Practices ของการทำ AWS Lambda Function (opens in a new tab)" href="https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html" target="_blank">Best Practices ของการทำ AWS Lambda Function</a> มีอยู่ข้อหนึ่งที่บอกว่า &#8220;Control the dependencies in your function&#8217;s deployment package.&#8221; คือ แนะนำให้เราควบคุมและจัดการกับ Library ที่ Funtion ของเราต้องใช้งาน</p>



<span id="more-5189"></span>



<p>เพราะเมื่อระบบเราใหญ่ขึ้น สมมติว่ามี 100 Functions และต้องเรียกใช้ Library คล้ายกัน ปัญหาคือ</p>



<ol class="wp-block-list"><li>เราจะควบคุม Version ของ Library ให้เหมือนกันทุก Function ได้ไหม</li><li>Filesize ของ Deployment Package เราจะใหญ่ เพราะ ทุก Function ต้องแพ็ค Library ที่เกี่ยวข้องติดไปด้วยทุกตัว</li><li>เมื่อ Deployment Package เราใหญ่ จะทำให้ช่วงเวลา Cold Start ของ Lambda เรา ช้าขึ้นไปอีก</li></ol>



<p>Amazon เลยมีฟีเจอร์หนึ่งให้ใช้คือ Lambda Layers เพื่อใช้เก็บ Libraries, Custom Runtime หรือพวก Dependencies ต่างๆ แยกออกจาก Function ของเรา เพื่อให้ Function เราเล็กลง และสามารถเรียกใช้ Layers ต่างๆที่ Function อื่นได้ด้วย</p>



<p style="text-align:center" class="has-background has-very-light-gray-background-color"><strong>คำแนะนำ: บทความนี้จะอ่านเข้าใจ ถ้าคุณใช้ AWS Lambda เบื้องต้นเป็นแล้ว</strong></p>



<h2 class="wp-block-heading">ปัญหา Lambda Layers กับ <g class="gr_ gr_4 gr-alert gr_gramm gr_inline_cards gr_run_anim Style multiReplace" id="4" data-gr-id="4">C# .</g>Net Core</h2>



<p>ในขณะที่ผมกำลังเขียนบล็อกนี้ (7 Feb 2019) ตัว AWS Lambda เอง รองรับการทำ Layers แค่ Node.js, Python, และ Ruby functions เท่านั้น </p>



<p>ส่วนภาษาอื่นๆ อาจจะลำบากหน่อย เพราะ Dependency อยู่ในโฟลเดอร์ข้างนอก project   เช่น C# ของ MacOS จะอยู่ใน <em>/usr/local/share/dotnet/</em> ซึ่งเราจะต้องสร้าง <a href="https://docs.microsoft.com/en-us/dotnet/core/deploying/runtime-store" target="_blank" rel="noreferrer noopener" aria-label="Runtime package store (opens in a new tab)">Runtime package store</a> ของ project นั้น แล้วไปคัดเลือก Dependency เอง จากนั้น zip file ออกมานำไปใช้ใน Lambda Layer ส่วนโค้ดของเรา เมื่อทำคำสั่ง publish แล้ว เราต้อง zip เฉพาะโค้ดเราเท่านั้น และนำไปขึ้น AWS เอง และทำการลิงค์กัน&#8230; ฟังดูยุ่งยากเนอะ</p>



<p>แต่อย่าเพิ่งตกใจไป ที่เขียนบล็อกนี้เพราะ Amazon กำลังปรับปรุงเครื่องมือ AWS CLI ของตัวเอง เพื่อให้ชาว C# .Net Core ทำทำ Layers ได้ง่ายๆแล้ว ซึ่งขณะนี้อยู่ในช่วงของ Beta Version เพิ่งปล่อย <a rel="noreferrer noopener" aria-label="Docs (opens in a new tab)" href="https://github.com/aws/aws-extensions-for-dotnet-cli/blob/lambda-layers/docs/Layers.md" target="_blank">Docs</a> มาให้ได้อ่านกันเพียง 20 วันนี้เอง</p>



<h2 class="wp-block-heading">Amazon Lambda Tools V.3.2.0-beta1  ที่มาพร้อมกับ คำสั่ง &#8220;<g class="gr_ gr_32 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling" id="32" data-gr-id="32">dotnet</g> lambda publish-layers&#8221;</h2>



<p>ถ้าอ่านถึงตรงนี้ ชาว C# น่าจะอยากใช้งาน Layer กันบ้างแล้ว แต่เราจะไม่สามารถใช้กับ AWS Lambda Tools เวอร์ชั่นปัจจุบันได้ (ตอนนี้คือ V.3.1.2) ดังนั้นต้องเอาตัว Beta มาใช้ ด้วยวิธีการ Build Package มันขึ้นมาเองเอง</p>



<p>สำหรับคนที่ไม่เคยใช้ AWS CLI จำเป็นต้องทำสองขั้นตอนนี้ก่อน คือ</p>



<ol class="wp-block-list"><li><a rel="noreferrer noopener" aria-label="ติดตั้ง AWS CLI (opens in a new tab)" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html" target="_blank">ติดตั้ง AWS CLI</a></li><li><a rel="noreferrer noopener" aria-label="สร้าง Configuration เพื่อให้ AWS User เรา ใช้ได้กับ AWS CLI (opens in a new tab)" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration" target="_blank">สร้าง Configuration เพื่อให้ AWS User เรา ใช้ได้กับ AWS CLI</a></li></ol>



<p>แต่ถ้าเรามี AWS CLI พร้อมใช้อยู่แล้ว ให้เริ่มติดตั้ง Amazon Lambda Tools กันเลยได้ให้ทำการ clone repo https://github.com/aws/aws-extensions-for-dotnet-cli.git </p>



<p>1.ให้ทำการ clone repo https://github.com/aws/aws-extensions-for-dotnet-cli.git </p>



<pre class="wp-block-code"><code>git clone https://github.com/aws/aws-extensions-for-dotnet-cli.git</code></pre>



<p>2. จากนั้นทำการ checkout branchไปที่ &#8220;lambda-layers&#8221; </p>



<pre class="wp-block-code"><code>git checkout lambda-layers</code></pre>



<p>3.สร้าง NuGet Package ใหม่ของ amazon.lambda.tools </p>



<pre class="wp-block-code"><code>dotnet msbuild buildtools/build.proj /t:build-nuget-packages</code></pre>



<p>4.ทำการ uninstall amazon.lambda.tools ตัวเก่าออก <em>(หากใครยังไม่เคยใช้มาก่อน ก็ไม่ต้องทำบรรทัดนี้)﻿</em></p>



<pre class="wp-block-code"><code>dotnet tool uninstall -g amazon.lambda.tools</code></pre>



<p>5.ติดตั้ง package amazon.lambda.tools ใหม่</p>



<pre class="wp-block-code"><code>dotnet tool install -g --add-source ./Deployment/nuget-packages/ Amazon.Lambda.Tools --version 3.2.0-beta1</code></pre>



<p>เมื่อติดตั้งเสร็จ ให้ลองพิมพ์ &#8220;dotnet lambda<strong>&#8220;</strong> ดูครับ จะต้องได้เวอร์ชั่น <strong>3.2.0-beta1</strong> และมีคำสั่ง <strong>publish-layer</strong> เพิ่มเข้ามา (นอกจากมี <strong>publish-layer</strong> แล้ว ยังทำให้คำสั่ง <strong>deploy-function</strong> และ <strong>package</strong> รองรับการใช้งาน layers ด้วย)</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="711" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-tools-dotnet-publish-layer-1200x711.png" alt="" class="wp-image-5201" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-tools-dotnet-publish-layer-1200x711.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-tools-dotnet-publish-layer-1024x607.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-tools-dotnet-publish-layer-768x455.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-tools-dotnet-publish-layer-600x356.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-tools-dotnet-publish-layer.png 1606w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>Amazon Lambda Tools Version 3.2.0-beta1</figcaption></figure>



<h2 class="wp-block-heading" id="mce_37">มาลองสร้าง Lambda Layers ของภาษา C# .Net Core กัน</h2>



<p>ก่อนอื่นเลย เราต้องมี S3 Bucket ก่อน เพราะว่าเมื่อทำคำสั่งนี้ มันจะเก็บ Package ต่างๆ ไว้ที่ S3 ซึ่งในที่นี้ผมตั้งชื่อว่า <strong>lambda-profile-dependency</strong></p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="337" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-1200x337.png" alt="" class="wp-image-5191" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-1200x337.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-1024x288.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-768x216.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-600x169.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3.png 2320w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>S3 ที่สร้างไว้ชื่อ lambda-profile-dependency</figcaption></figure></div>



<p>จากนั้นเข้าไปที่โฟลเดอร์ C# Project ของเรา และเราจะใช้คำสั่ง dotnet lambda publish-layer ซึ่งมีรูปแบบการใช้งานดังนี้</p>



<pre class="wp-block-code"><code>dotnet lambda publish-layer layer-name --layer-type runtime-package-store --s3-bucket bucket-name</code></pre>



<p>โดย</p>



<ul class="wp-block-list"><li><strong>layer-name</strong>&nbsp;คือ ชื่อของ Layer ที่เราต้องการ </li><li><strong>bucket-name</strong>&nbsp;คือ ชื่อของ S3 Bucket ที่เราจะใช้เก็บไฟล์</li><li><strong>runtime-package-store</strong>&nbsp;คือ type ของ layer ซึ่งคำสั่งนี้ถูกทำเผื่อไว้ในอนาคต ว่าจะมีประเภทของ package ให้เลือกได้ ตอนนี้มีให้ใช้อย่างเดียวคือ runtime package</li></ul>



<p>ในที่นี้ ถ้าผมต้องการเก็บลง S3 Bucket ชื่อ <strong>lambda-profile-dependency</strong> และตั้งชื่อ Layer ว่า <strong>profile-lib</strong> จะได้คำสั่งประมาณนี้</p>



<pre class="wp-block-code"><code>dotnet lambda publish-layer profile-lib --layer-type runtime-package-store --s3-bucket lambda-profile-dependency﻿</code></pre>



<p>เมื่อรันคำสั่ง มันก็จะทำการแพ็คไฟล์นั่นนู่นนี่ไปเรื่อยๆ จนสุดท้าย ออกมาจะต้องได้ ARN ของ AWS Lambda Layer ที่เราจะใช้งาน ตามภาพด้านล่าง (บรรทัดสุดท้าย)</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="588" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-arn-1200x588.png" alt="" class="wp-image-5192" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-arn-1200x588.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-arn-1024x502.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-arn-768x376.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-arn-600x294.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-arn.png 1470w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure></div>



<p>ให้เราคัดลอก ARN ไปใช้ในคำสั่ง deploy lambda ด้วยรูปแบบดังนี้</p>



<pre class="wp-block-code"><code>dotnet lambda deploy-function function-name --function-layers layer-arn﻿</code></pre>



<p>โดย</p>



<ul class="wp-block-list"><li><strong>function-name</strong>&nbsp;คือ ชื่อ Lambda Function ของเรา</li><li><strong>layer-arn</strong>&nbsp;คือ ARN ของ Lambda Layer ที่เราจะลิงค์ถึง (รูปแบบจะประมาณนี้ arn:aws:lambda:[region-name]:[aws-user-id]:layer:[layer-name]:[layer-version])</li></ul>



<p>ดังนั้น ถ้าผมต้องการ Function ชื่อ list_profile และ ใช้ layer ที่เพิ่งสร้างขึ้นเมื่อสักครู่ จะใช้คำสั่งดังนี้</p>



<pre class="wp-block-code"><code>dotnet lambda deploy-function list_profile –-function-role MyRole --function-layers arn:aws:lambda:ap-southeast-1:819684245502:layer:profile-lib:1﻿</code></pre>



<p>เมื่อรันคำสั่ง มันก็จะทำการแพ็คไฟล์ เพื่อเตรียม deploy โดยมันจะแยกเอาเฉพาะไฟล์ Function ของเราเท่านั้น ไม่เอาพวก dependency library เข้าไปด้วย (ขั้นตอนนี้ ทำมือเองได้ แต่ใช้คำสั่งเถอะ เร็วกว่า)</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="345" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-deploy-1200x345.png" alt="" class="wp-image-5193" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-deploy-1200x345.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-deploy-1024x294.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-deploy-768x221.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-deploy-600x172.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-deploy.png 2688w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure></div>



<p>เป็นอันว่าเสร็จเรียบร้อย Lambda Function ของเรา กับ Lambda Layer ถูกนำขึ้นไปไว้บน AWS และผูกมิตรกันให้ด้วย</p>



<h2 class="wp-block-heading">สำรวจผลลัพธ์ Lambda Function และ Lambda Layer หลัง Deployment</h2>



<p>ลองมาดูกันว่าผลลัพธ์หลังจากการใช้งาน Lambda Layer เป็นอย่างไร</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="493" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layered-and-not-1200x493.png" alt="" class="wp-image-5195" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layered-and-not-1200x493.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layered-and-not-1024x421.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layered-and-not-768x316.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layered-and-not-600x247.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layered-and-not.png 2632w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>แต่เดิม list_profile ของผมมีขนาด 2.6 MB เหมือนกับตัวอื่นๆ ตอนนี้เหลือเพียง 15.9kB ลดลงไป 93.88%<br></figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="442" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-all-layered-and-not-1200x442.png" alt="" class="wp-image-5206" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-all-layered-and-not-1200x442.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-all-layered-and-not-1024x377.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-all-layered-and-not-768x283.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-all-layered-and-not-600x221.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-all-layered-and-not.png 2600w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>ลองใช้ Lambda Layer กับทุก Function ที่จะใช้งาน ไฟล์เล็กลงไปเยอะ เหลือไม่เกินตัวละ 15kB </figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1020" height="470" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layer-and-no-layer.png" alt="" class="wp-image-5205" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layer-and-no-layer.png 1020w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layer-and-no-layer-768x354.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-compare-layer-and-no-layer-600x276.png 600w" sizes="auto, (max-width: 1020px) 100vw, 1020px" /><figcaption>อันนี้ลองทดสอบแบบกากๆ (กดมือเอง) แบบที่มี layer และไม่มี layer ผลลัพธ์ที่ได้ ดูเหมือนจะไม่ต่างมาก <br>เพราะฟังก์ชันที่ทดสอบ โค้ดมันเล็กมากๆ (get data ด้วย id จาก MySQL บน RDS)</figcaption></figure></div>



<p style="color:#0570ae;text-align:center" class="has-text-color"><strong>เพิ่มเติม: อย่าลืมว่า การคิดเงินของ AWS Lambda มันคิดที่หน่วย 100ms นะ </strong><br><strong>อย่าง cold start จากที่โดนคิดเงิน 16000ms จะเหลือแค่ 15500ms&nbsp;หายไป&nbsp;500ms</strong></p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="490" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-1200x490.png" alt="" class="wp-image-5197" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-1200x490.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-1024x418.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-768x314.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-600x245.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer.png 2590w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>มีชั้น Layers เพิ่มมา 1 ตัว<br></figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="162" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-list-1200x162.png" alt="" class="wp-image-5198" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-list-1200x162.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-list-1024x138.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-list-768x104.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-list-600x81.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-layer-list.png 2620w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>Layer ที่เพิ่มเข้ามา ก็คือ layer ที่เราทำการสร้างใหม่ด้วยคำสั่ง publish-layer ซึ่งจะเป็น version 1  ถ้าเรา publish-layer ซ้ำไปอีกครั้งจะเป็น version 2</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="245" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-env-1200x245.png" alt="" class="wp-image-5196" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-env-1200x245.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-env-1024x209.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-env-768x157.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-env-600x123.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-env.png 2624w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>มี Environment Variable เพิ่มมาตัวหนึ่ง เพื่อระบุ PATH ว่า Library เราเอาไปไว้ที่ไหน<br></figcaption></figure></div>



<p style="text-align:center" class="has-text-color has-vivid-red-color">คำเตือน: ถ้าเราเคยมี Environment Variable เดิมอยู่ มันจะหายไป!!! </p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="625" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-list-file-code-1200x625.png" alt="" class="wp-image-5199" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-list-file-code-1200x625.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-list-file-code-1024x533.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-list-file-code-768x400.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-list-file-code-600x312.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-list-file-code.png 1590w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>ลองดูไฟล์ใน Function จะเหลือแค่โค้ดของเราเท่านั้น ไม่มีพวก DLL อื่นๆ ติดมาด้วย</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="507" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-detail-1200x507.png" alt="" class="wp-image-5200" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-detail-1200x507.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-detail-1024x432.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-detail-768x324.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-detail-600x253.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-layer-added-detail.png 2794w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>มาดูในหน้า Lambda Layer ก็จะเจอ Dependency ที่เราเพิ่งทำการนำขึ้นไป</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="441" src="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-uploaded-1200x441.png" alt="" class="wp-image-5202" srcset="https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-uploaded-1200x441.png 1200w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-uploaded-1024x376.png 1024w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-uploaded-768x282.png 768w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-uploaded-600x220.png 600w, https://myifew.com/wp-content/uploads/2019/02/aws-lambda-dotnet-layers-s3-uploaded.png 2642w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>ลองไปดูใน S3 Bucket ที่มี Lambda Layer พบโฟลเดอร์และเก็บไฟล์ packages.zip, artifact.xml ไว้</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1200" height="813" src="https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-07-at-15.28.27-1200x813.png" alt="" class="wp-image-5204" srcset="https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-07-at-15.28.27-1200x813.png 1200w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-07-at-15.28.27-1024x694.png 1024w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-07-at-15.28.27-768x520.png 768w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-07-at-15.28.27-600x406.png 600w, https://myifew.com/wp-content/uploads/2019/02/Screen-Shot-2562-02-07-at-15.28.27.png 2046w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /><figcaption>ลอง unzip packages.zip ออกมา พบโฟลเดอร์และ ไฟล์ DLL หน้าตาเหมือนกับใน <a href="https://docs.microsoft.com/en-us/dotnet/core/deploying/runtime-store" target="_blank" rel="noreferrer noopener" aria-label="runtime package store (opens in a new tab)">runtime package store</a> นั่นเอง</figcaption></figure></div>



<h2 class="wp-block-heading" id="mce_123">สรุป</h2>



<p>ถ้าชาว C# .Net Core อยากใช้ Lambda Layer ก็ลองทำตามในบล็อกนี้ได้เลย หรือไม่ก็รอ AWS CLI เวอร์ชั่น 3.2.0 ออกตัวเต็ม ก็ได้ครับ แต่เท่าที่ลองใช้ ไม่มีปัญหาอะไรนะ สะดวกดี</p>



<p>และข้อจำกัดตอนนี้ คือ Lambda Layer มีให้ใช้กันที่ 5 Layers ดังนั้น ต้องมาคิดต่อว่าจะจัดสรร Dependency กันอย่างไร เพื่อให้ใช้ประโยชน์ร่วมกันเยอะที่สุด</p>



<p><strong>อ้างอิง</strong></p>



<ul class="wp-block-list"><li>https://github.com/aws/aws-extensions-for-dotnet-cli/blob/lambda-layers/docs/Layers.md</li><li>https://github.com/aws/aws-extensions-for-dotnet-cli/tree/lambda-layers</li><li>https://github.com/aws/aws-extensions-for-dotnet-cli/issues/58</li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/5189/working-with-aws-lambda-layers-and-csharp-net-core2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>มาลอง Code Coverage บน  .Net Core 2+ ด้วย coverlet</title>
		<link>https://myifew.com/5145/code-coverage-on-net-core-2-via-coverlet/</link>
					<comments>https://myifew.com/5145/code-coverage-on-net-core-2-via-coverlet/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Thu, 20 Dec 2018 09:09:35 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[Automation Testing]]></category>
		<category><![CDATA[Automation Tools]]></category>
		<category><![CDATA[Code Coverage]]></category>
		<category><![CDATA[Code Quality]]></category>
		<category><![CDATA[Testing Tools]]></category>
		<category><![CDATA[Unit Test]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=5145</guid>

					<description><![CDATA[ใครที่เขียนโค้ดด้วย .Net Core 2+ และทำ CI/CD บน Linux ค่อนข้างยุ่งยากหน่อย เพราะหาเครื่องมือสำหรับตรวจสอบ Code Coverage ฟรีๆ ดีๆ ได้น้อยเหลือเกิน ที่ผมเคยใช้ดีๆ ก็มี minicover&#8230;]]></description>
										<content:encoded><![CDATA[
<p>ใครที่เขียนโค้ดด้วย .Net Core 2+ และทำ CI/CD บน Linux ค่อนข้างยุ่งยากหน่อย เพราะหาเครื่องมือสำหรับตรวจสอบ Code Coverage ฟรีๆ ดีๆ ได้น้อยเหลือเกิน ที่ผมเคยใช้ดีๆ ก็มี <a rel="noreferrer noopener" aria-label="minicover (opens in a new tab)" href="https://github.com/lucaslorentz/minicover" target="_blank">minicover</a> จนเพิ่งมาลองเล่นอีกตัว ชื่อ <a rel="noreferrer noopener" aria-label="coverlet (opens in a new tab)" href="https://github.com/tonerdo/coverlet" target="_blank">coverlet</a> ใช้งานได้ง่ายกว่าเยอะมาก เพราะมันทำงานร่วมกับ MSTest ได้เลย</p>



<span id="more-5145"></span>



<h2 class="wp-block-heading">วิธีติดตั้ง</h2>



<p>การใช้งานมี 2 แบบครับ คือใช้คำสั่ง coverlet ทำงานเลย ให้ติดตั้งด้วยคำสั่ง</p>



<pre class="wp-block-preformatted">dotnet tool install --global coverlet.console</pre>



<p>หรือทำงานผ่านคำสั่ง dotnet test ให้เพิ่ม nuget package ด้วยคำสั่ง</p>



<pre class="wp-block-preformatted">dotnet add package coverlet.msbuild</pre>



<h2 class="wp-block-heading">วิธีใช้งาน</h2>



<p>ในตัวอย่างนี้ โครงสร้างโปรเจ็คผมจะหน้าตาตามรูปภาพด้านล่าง และผมเข้าจะยกตัวอย่างคำสั่งทั้งหมดกรณีที่อยู่ในโฟลเดอร์ /tests/web.UnitTest/<br></p>



<figure class="wp-block-image is-resized"><img loading="lazy" decoding="async" src="https://myifew.com/wp-content/uploads/2018/12/netcore2-project-structure-453x1024.png" alt="" class="wp-image-5146" width="227" height="512"/></figure>



<p>ดังนั้น เมื่อต้องการดู Code Coverage ผ่านคำสั่ง coverlet</p>



<pre class="wp-block-verse">coverlet <strong>bin/Debug/netcoreapp2.1/api.dll</strong> --target "dotnet" --targetargs "<strong>test web.UnitTest.csproj --no-build</strong>" --output "<strong>../web.UnitTest.Results/Coverage.xml</strong>"&nbsp;--format <strong>opencover</strong></pre>



<p>ซึ่งตัวหนาคือข้อมูลของโปรเจ็คเราเอง โดยผู้อ่านสามารถแก้ไขตามที่จะนำไปใช้งานได้เลย</p>



<ul class="wp-block-list"><li><strong>bin/Debug/netcoreapp2.1/api.dll</strong> : คือ ที่อยู่ของไฟล์ Unit Test Binary ที่เรา Build มาแล้ว</li><li><strong>test web.UnitTest.csproj &#8211;no-build</strong> : คือ arguments เพื่อบอกว่าเราต้องการทดสอบด้วย MSTest ไปที่ File Project ไหน, และผมเพิ่มไปว่า ไม่ต้อง ทำการ build project ใหม่ เพราะผมสั่ง build ไปแล้ว (ถ้าใครไม่สั่ง build ก่อน ให้เอา &#8211;no-build ออกด้วยนะ)</li><li><strong>../web.UnitTest.Results/Coverage.xml</strong> : คือ ที่อยู่ของไฟล์ report ซึ่งผมให้ใช้เป็น Formet ของ OpenCover จึงจ้องมีนามสกุล .xml (ค่ามาตรฐาน ถ้าไม่ระบุ มันจะเป็น JSON)</li><li></li><li><strong>opencover</strong> : คือ Format ของไฟล์ report ที่เราจะนำไปใช้งานต่อ</li></ul>



<p>ลองมาดูการใช้งานผ่านคำสั่ง dotnet test แบบปกติบ้าง</p>



<pre class="wp-block-preformatted">dotnet test --no-restore --no-build --logger:"trx;LogFileName=Results.trx" --results-directory:"../web.UnitTest.Results" <strong>/p:CollectCoverage=true</strong> /p:CoverletOutput="<strong>../web.UnitTest.Results/Coverage.xml</strong>" /p:CoverletOutputFormat=<strong>opencover</strong></pre>



<p>ถ้าให้เดาค่าต่างๆ คงพอเดากันได้นะครับ ระบุเหมือนด้านบนเลย แต่คำสั่งหนึ่งที่ต้องมีเสมอคือ</p>



<pre class="wp-block-preformatted">/p:CollectCoverage=true </pre>



<p>เพื่อบ่งบอกว่า เราต้องการ Generate Code Coverage ออกมาด้วย</p>



<h2 class="wp-block-heading">การแสดงผล</h2>



<p>เมื่อรันคำสั่งตามด้านบนเรียบร้อยแล้ว จะได้หน้าตาประมาณนี้ครับ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="540" src="https://myifew.com/wp-content/uploads/2018/12/Screen-Shot-2561-12-20-at-13.50.06-1200x540.png" alt="" class="wp-image-5147" srcset="https://myifew.com/wp-content/uploads/2018/12/Screen-Shot-2561-12-20-at-13.50.06-1200x540.png 1200w, https://myifew.com/wp-content/uploads/2018/12/Screen-Shot-2561-12-20-at-13.50.06-1024x461.png 1024w, https://myifew.com/wp-content/uploads/2018/12/Screen-Shot-2561-12-20-at-13.50.06-768x346.png 768w, https://myifew.com/wp-content/uploads/2018/12/Screen-Shot-2561-12-20-at-13.50.06-600x270.png 600w, https://myifew.com/wp-content/uploads/2018/12/Screen-Shot-2561-12-20-at-13.50.06.png 1990w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>ความหมายที่ได้จากการแสดงผล คือ</p>



<ul class="wp-block-list"><li><strong>Module</strong> : ชื่อ System ที่มันทำการทดสอบ</li><li><strong>Method coverage</strong> : จำนวนของ Function/Method ที่ถูกเรียกใช้งาน</li><li><strong>Branch coverage</strong> : จำนวนของ Branch หรือเงื่อนไขใน if statement ถูกเรียกใช้งานทั้ง true และ false</li><li><strong>Line coverage</strong> : จำนวนของบรรทัดที่ถูกเรียกใช้งาน</li></ul>



<pre class="wp-block-preformatted"><em>อ้างอิงคำอธิบายจาก <a href="http://www.somkiat.cc/introduction-to-code-coverage/">http://www.somkiat.cc/introduction-to-code-coverage/</a></em></pre>



<h2 class="wp-block-heading">กำหนดคุณภาพ ด้วยการตั้ง Threshold</h2>



<p>คราวนี้ ถ้าเราอยากตั้งค่าว่า ถ้า Code Coverage เรามี Test ครอบคลุมต่ำกว่า xx% เราจะไม่ให้ผ่านการทดสอบ โดยเพิ่ม Argument ชื่อ threshold ลงไป ดังนี้ </p>



<p>ตัวอย่างด้วยคำสั่ง coverlet</p>



<pre class="wp-block-preformatted">coverlet bin/Debug/netcoreapp2.1/api.dll --target "dotnet" --targetargs "test web.UnitTest.csproj --no-build" --output "../web.UnitTest.Results/Coverage.json" <strong>--threshold 80</strong></pre>



<p>ตัวอย่างด้วยคำสั่ง dotnet test</p>



<pre class="wp-block-preformatted">dotnet test --no-restore --no-build --logger:"trx;LogFileName=Results.trx" --results-directory:"../web.UnitTest.Results" /p:CollectCoverage=true /p:CoverletOutput="../web.UnitTest.Results/Coverage.xml" /p:CoverletOutputFormat=opencover <strong>/p:Threshold=80 /</strong></pre>



<p>ซึ่งในตัวอย่างผมกำหนดว่า Coverage ทุกประเภทจะต้องเกิน 80% ถึงจะเรียกว่าผ่าน ถ้าต่ำกว่านั้นจะ Fail ทันที ดังรูปด้านล่าง</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="594" src="https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-1-1200x594.png" alt="" class="wp-image-5149" srcset="https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-1-1200x594.png 1200w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-1-1024x507.png 1024w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-1-768x380.png 768w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-1-600x297.png 600w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-1.png 2134w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<p>และเราสามารถกำหนดเฉพาะ Coverage ได้ด้วย เช่นผมต้องการเช็คเฉพาะ Line Coverage ก็สามารถเพิ่ม Argument เข้าไป ดังนี้</p>



<p>ตัวอย่างด้วยคำสั่ง coverlet</p>



<pre class="wp-block-preformatted"><strong>--threshold-type line</strong></pre>



<p>ตัวอย่างด้วยคำสั่ง dotnet test</p>



<pre class="wp-block-preformatted"> <strong>/p:ThresholdType=line</strong></pre>



<p>ผลที่ได้คือ</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1200" height="509" src="https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-by-type-1200x509.png" alt="" class="wp-image-5150" srcset="https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-by-type-1200x509.png 1200w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-by-type-1024x434.png 1024w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-by-type-768x326.png 768w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-by-type-600x254.png 600w, https://myifew.com/wp-content/uploads/2018/12/coverlet-under-threshold-standard-by-type.png 2142w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></figure>



<h2 class="wp-block-heading">สรุป</h2>



<p>ถ้าเทียบกับ minicover แล้ว ตัวนี้ค่อนข้างใช้งานง่าย ไม่ซับซ้อนมากนัก รองรับ .Net Core 2.2.1 ตัวล่าสุดด้วย และสามารถแจกแจงรายละเอียด ตาม Coverage Type ต่างๆ พร้อมออก Report ได้ เช่น json, lcov, opencover (xml file), cobertura (xml file) และ teamcity ถ้าสนในรายละเอียดมากกว่านี้ ไปดูได้ที่ Github ของผู้พัฒนาได้เลยครับ ที่ https://github.com/tonerdo/coverlet</p>



<p><em>Featured image from: https://www.aapnainfotech.com/test-coverage-much-testing-enough</em>/</p>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/5145/code-coverage-on-net-core-2-via-coverlet/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>เมื่อ Unit Test ต้องทดสอบร่วมกับระบบอื่นๆ จะแก้ปัญหาอย่างไร?.. มารู้จัก Test Doubles กัน</title>
		<link>https://myifew.com/4556/how-to-skip-dependency-in-unit-test-by-test-doubles/</link>
					<comments>https://myifew.com/4556/how-to-skip-dependency-in-unit-test-by-test-doubles/#respond</comments>
		
		<dc:creator><![CDATA[iFew]]></dc:creator>
		<pubDate>Thu, 05 Jul 2018 18:21:41 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[Automation Testing]]></category>
		<category><![CDATA[Test Doubles]]></category>
		<category><![CDATA[Testable]]></category>
		<category><![CDATA[Unit Test]]></category>
		<guid isPermaLink="false">https://myifew.com/?p=4556</guid>

					<description><![CDATA[หลังจากเขียนเรื่อง Unit Test ไปก็หลายบล็อก ไล่ตั้งแต่เรื่องพื้นฐานอย่าง การออกแบบระบบให้รองรับการทดสอบ (Testable) มาจนถึง วิธีการคิดและลงมือทำ Automated Test ด้วย Unit Test และ รูปแบบการทดสอบของ Unit Test ก็ยังเหลือเรื่องหนึ่งที่สำคัญมากและหลายคนอาจจะสงสัยตอนที่ทำ Unit Test นั่นก็คือ "จะทำอย่างไร เมื่อสิ่งที่เราทดสอบ อย่าง Class, Function มันต้องไปทำงานร่วมกับสิ่งอื่นๆ เช่น Class อื่นๆ, Function อื่นๆ, Database, API ตัวอื่นๆ ของเรา หรือ API ภายนอกระบบ" ..]]></description>
										<content:encoded><![CDATA[<p>หลังจากเขียนเรื่อง Unit Test ไปก็หลายบล็อก ไล่ตั้งแต่เรื่องพื้นฐานอย่าง <a href="https://myifew.com/4414/design-project-code-structure-for-testable/">การออกแบบระบบให้รองรับการทดสอบ (Testable)</a> มาจนถึง <a href="https://myifew.com/4455/design-and-develop-code-for-testable/">วิธีการคิดและลงมือทำ Automated Test ด้วย Unit Test</a> และ <a href="https://myifew.com/4480/unit-test-solitary-or-sociable/">รูปแบบการทดสอบของ Unit Test</a> ก็ยังเหลือเรื่องหนึ่งที่สำคัญมากและหลายคนอาจจะสงสัยตอนที่ทำ Unit Test นั่นก็คือ &#8220;จะทำอย่างไร เมื่อสิ่งที่เราทดสอบ อย่าง Class, Function มันต้องไปทำงานร่วมกับสิ่งอื่นๆ เช่น Class อื่นๆ, Function อื่นๆ, Database, API ตัวอื่นๆ ของเรา หรือ API ภายนอกระบบ&#8221; ..<span id="more-4556"></span></p>
<h2>ทบทวน &#8220;5 คุณสมบัติของ Unit Test ที่ดี&#8221; อีกครั้ง</h2>
<p>ก่อนจะไปต่อ ขอทบทวนคุณสมบัติที่ดีของ 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> ทำงานได้เร็ว</li>
<li><strong>Isolated:</strong> เป็นอิสระจากกัน</li>
<li><strong>Repeatable:</strong> ทำซ้ำได้ ไม่ขึ้นกับระบบอื่นๆ เช่น API ภายนอก, Database, File System</li>
<li><strong>Self-Verify:</strong> แสดงให้เห็นผลการทดสอบ ผ่าน หรือไม่ผ่านได้อย่างชัดเจน</li>
<li><strong>Timely:</strong> เขียนให้ถูกเวลา คือ ควรมี Test ก่อน เพื่อให้ได้เข้าใจปัญหา และจึงเขียนโค้ดเข้ามาแก้ปัญหานั้น</li>
</ul>
<p>คราวนี้ การที่ Unit Test ของเราต้องไปทำงานร่วมกับสิ่งอื่นๆ มันมักทำให้ขัดแย้งกับ 5 ข้อ ข้างต้น เช่น</p>
<ul>
<li>เมื่อเราต้องการทดสอบ Function ที่ต้องต่อกับ Database มันก็จะทำงานไม่เร็ว เพราะ Query นาน, มันทำซ้ำไม่ได้ เพราะต้องเพิ่มลบข้อมูลจาก Database ตลอด แล้วไหนจะกระทบตัวอื่นๆที่อาจจะใช้ Database เดียวกันด้วย</li>
<li>เมื่อเราต้องการทดสอบ Function ที่ต้องต่อกับ API ภายในของเราเอง หรือภายนอก มันก็ทำงานไม่เร็ว เพราะมีเรื่องของความเร็ว Network/Internet ซึ่งถ้า Internet ล่ม ก็ทำซ้ำไม่ได้ หรือถ้าส่งข้อมูลไปให้แล้วบาง API ก็ส่งข้อมูลซ้ำไม่ได้ เช่น ส่งข้อมูลไป API สมัครสมาชิกที่มีเงื่อนไขสมัครด้วยอีเมลเดียวเท่านั้น</li>
<li>เมื่อเราต้องการทดสอบ Function ที่ต้องไปทำงานร่วมกับ Function อื่น แต่มันยังทำไม่เสร็จหรือไม่ได้ทำ เราก็ทดสอบไม่ได้อยู่ดี</li>
</ul>
<p>ดังนั้น ถ้าต้องการให้ Unit Test ของเราทำงานได้ตามคุณสมบัติดังกล่าว เราจะต้องสร้างการทำงานสิ่งอื่นๆที่เกี่ยวข้องขึ้นมา ซึ่ง จะมีอยู่  5 ประเภท เรียกรวมๆ ว่า Test Doubles</p>
<h2>Test Doubles คืออะไร</h2>
<p style="text-align: center;"><img loading="lazy" decoding="async" class="alignnone size-large wp-image-4565 aligncenter" src="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-1200x530.png" alt="" width="1024" height="452" srcset="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-1200x530.png 1200w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-1024x452.png 1024w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-768x339.png 768w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-600x265.png 600w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-700x309.png 700w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles.png 1296w" sizes="auto, (max-width: 1024px) 100vw, 1024px" />Image from: https://blogs.sap.com/</p>
<p>จริงๆ เราอาจเคยทำมันมาแล้วอย่างใดอย่างหนึ่ง แต่เรามักเรียกกันตามแต่ถนัด เช่น Mock, ระบบจำลอง, ระบบทดสอบ ฯลฯ อะไรก็ว่าไป แต่ที่พบคือ Mock มักถูกใช้บ่อยสุด</p>
<p>คำว่า Test Doubles มาจากหนังสือที่ลุง <a href="http://xunitpatterns.com/Test%20Double%20Patterns.html">Gerard Meszaros</a> เขาเขียนมันขึ้นมา โดยแปลงมาจากคำว่า Stunt Double ที่ใช้ในการทำหนัง ซึ่ง Stunt Double คือ สตั๊นแมนหรือนักแสดงที่มีหน้าตา รูปร่าง แต่งตัวคล้ายกับพระเอก แล้วออกไปแสดงแทนพระเอกในบางฉาก</p>
<p>คราวนี้พอมาเป็น Test Doubles โดยอธิบายมุมเดียวกับ Stunt Double มันก็คือ อะไรบางอย่างที่เราทำมันขึ้นมาเพื่อให้มันทำงานแทนของจริง ในขณะที่เราไม่ได้ต้องการทดสอบมัน อย่างเช่น API ของคนอื่น, Class/Function ของคนอื่น, Database  เป็นต้น</p>
<h2>Test Doubles 5 ประเภท มีอะไรบ้าง</h2>
<p>อย่างที่บอก ว่าเราเคยเรียกทุกสิ่งที่ถูกจำลองว่า Mock  แต่เมื่อพิจารณาดีๆ มันมีจุดแตกต่างกัน ซึ่งเราจะใช้การเรียกประเภทต่างๆ อ้างอิงตามที่ลุง Gerard Meszaros ได้เขียนอธิบายไว้ และทั่วโลกก็ใช้เรียกตามนี้เช่นกัน ดังนั้นเวลาใช้สื่อสารเราจะได้เข้าใจตรงกัน</p>
<p>ปล. ตัวอย่างที่จะใช้ต่อไปนี้ เขียนด้วย .Net Core 2</p>
<h3>1. Dummy</h3>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4572 aligncenter" src="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Dummy.png" alt="" width="722" height="271" srcset="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Dummy.png 722w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Dummy-600x225.png 600w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Dummy-700x263.png 700w" sizes="auto, (max-width: 722px) 100vw, 722px" /></p>
<p>แปลตรงตัวคือ หุ่นเชิด, ของหลอกตา, คนโง่ เหมือนกับที่เคยมีข่าวว่ากล้องดัมมี่ คือมีแต่กล่องแปะไว้หลอกตาก็อุ่นใจ เช่นกัน ใน Test Doubles มันคือ อะไรก็ได้ ที่ใส่ไปแล้วระบบเราสามารถทำงานได้ หรือคอมไพล์ผ่าน</p>
<p>ถามว่าโง่ได้ขนาดไหน ก็เช่น สร้าง Class เปล่าๆ หรือ Function ที่คืนค่า null หรือ throw exception ก็ยังได้ โดยไม่ต้องสนใจว่ามันทำงานอย่างไร เราสนแค่ว่า ตัวที่เราจะทดสอบสามารถเรียกมันแล้วผ่านไปได้โดยไม่ติดขัด</p>
<p><strong>ตัวอย่าง Dummy</strong></p>
<p>ผมมีโค้ดสำหรับการ Login โดย Constructor Member() จะเป็นส่ง Username/Password ไปตรวจสอบ ซึ่งถ้าถูกต้อง ก็จะทำงานต่างๆ ได้ เช่น แสดงข้อความ &#8220;Welcome to member area&#8221; ใน method Profile()</p>
<p>View the code on <a href="https://gist.github.com/ifew/e9a674894ac6f3101f2c11ea36d47544">Gist</a>.</p>
<p>ผมสร้าง Interface ขึ้นมาชื่อ IAuthorize และคลาสที่ถูกนำไปใช้งานคือ Authorize โดยภายในนั้นมี CheckAuthorize() ซึ่งจะทำการตรวจสอบว่า Login ถูกต้องหรือไม่</p>
<p>และผมสร้างคลาสอีกตัวชื่อ DummyAuthorize เพื่อที่จะใช้ทดสอบ โดยผมไม่ได้ return อะไรออกไปว่าเป็น true/false มีเพียงส่งค่าว่างเปล่าประเภท Boolean ออกไป</p>
<p>View the code on <a href="https://gist.github.com/ifew/a5c31b52c3488e5c65d97cef13d78e48">Gist</a>.</p>
<p>มาดูในไฟล์ Unit Test ซึ่งผมต้องการตรวจสอบว่าสามารถสร้าง Object ของ Member ได้หรือไม่ แต่ติดว่า มันต้องใส่ Username/Password ใน Constructor ด้วยทุกครั้ง ดังนั้น ผมจึงใช้ DummyAuthorize แทน เพื่อให้มันทำงานทะลุผ่านไปได้</p>
<p>View the code on <a href="https://gist.github.com/ifew/1d96adc587c5d57a0e4b7c28a4fb57ed">Gist</a>.</p>
<p>นี่แหละครับ การทำงานของ Dummy ใส่อะไรไปแล้วใช้แทน Dependency ตัวอื่นๆ ได้ เพื่อทะลุไปทำงานอย่างอื่นต่อตามที่ต้องการ ซึ่งในตัวอย่างนี้ ผมลองเช็ค Type Object  Member ดู</p>
<h3>2. Stub</h3>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4564 aligncenter" src="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Stub.png" alt="" width="777" height="271" srcset="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Stub.png 777w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Stub-600x209.png 600w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Stub-768x268.png 768w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Stub-700x244.png 700w" sizes="auto, (max-width: 777px) 100vw, 777px" /></p>
<p>Stub คือการกำหนดสถานะหรือสิ่งต่างๆให้เป็นไปตามที่เราต้องการ (<strong>State verification</strong>) โดยหน้าตาจะคล้ายกับ Dummy แต่ Stub เราจะต้องกำหนดค่าที่จะใช้ด้วย เพื่อให้ได้ผลลัพธ์ออกมาตามที่เราต้องการ</p>
<p>เช่น Test Case ของสมการ 1 + X จะต้องได้เท่ากับ 2 โดยที่ X คือ Function ภายนอกที่เราไปเรียกใช้ และผลลัพธ์จะต้องออกมาเป็น 2 เสมอ ดังนั้น เราจะต้องกำหนดค่า X ว่าต้องเป็น 1 เท่านั้น (ซึ่งต่างจาก Dummy ที่ไม่สนใจการระบุค่า)</p>
<p>และที่เราชอบเรียกกันว่า Mock ส่วนมากคือการทำ Stub นี่เอง เพราะเราเขียนโค้ดขึ้นมาเพื่อให้ได้ค่าอย่างใดอย่างหนึ่งนำไปใช้งานต่อ และแสดงผลลัพธ์ปลายทางที่เราต้องการ</p>
<p><strong>ตัวอย่าง Stub</strong></p>
<p>ผมอ้างอิงจากโค้ดชุดเก่าของ Dummy แต่เปลี่ยน Unit Test ใหม่ โดยผมต้องการตรวจสอบว่า เมื่อเรียกใช้ Profile() จะต้องแสดงข้อความ &#8220;Welcome to member area&#8221; ขึ้นมา</p>
<p>View the code on <a href="https://gist.github.com/ifew/2a112ce61cb112ffec6f997aca415403">Gist</a>.</p>
<p>และเช่นกัน ผมสร้าง Class ชื่อ <span class="pl-en">StubAuthorize</span> จาก <span class="pl-en">IAuthorize </span>และใน CheckAuthorize() จะระบุเป็น true เสมอ เพื่อให้ Unit Test ผมแสดงข้อความ <span class="pl-pds">&#8220;</span>Welcome to member area<span class="pl-pds">&#8221; ขึ้นเสมอ</span></p>
<p>View the code on <a href="https://gist.github.com/ifew/0825095f960938e5dd3c7b60df80f786">Gist</a>.</p>
<p>อาจจะมีความสงสัยว่า ถ้าเราใส่ Username/Password เข้าไปตอนสร้าง object Member เลยได้ไหม อย่างไรก็คืนค่าเป็น  true เสมอ ตอบเลยว่าได้ แต่มีจุดให้คิดเพิ่ม คื อ</p>
<ol>
<li>เราต้องการตรสวจสอบการเรียกใช้ Profile() ว่าแสดงผลถูกต้องหรือไม่ ในสถานะที่เราต้องการให้มันทำงานถูกต้อง ดังนั้นเราไม่ได้ต้องการตรวจสอบ CheckAuthorize() ว่าทำงานถูกต้องหรือไม่ อันนี้จะเขียนเป็นอีก Unit Test หนึ่ง (แต่ก็ไม่ผิดนะครับ เพราะเป็นรูปแบบ Unit Test &#8211; Sociable ซึ่งลองอ่านได้ที่ <a href="https://myifew.com/4480/unit-test-solitary-or-sociable/">รูปแบบการทดสอบของ Unit Test</a>)</li>
<li>แน่ใจหรือไม่ว่า Username/Password ที่ใส่ค้างไว้ จะไม่เปลี่ยนในภายหลัง</li>
</ol>
<h2>3. Mock</h2>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4562 aligncenter" src="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Mock.png" alt="" width="716" height="131" srcset="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Mock.png 716w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Mock-600x110.png 600w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Mock-700x128.png 700w" sizes="auto, (max-width: 716px) 100vw, 716px" /></p>
<p>ชื่อนี้สร้างความสับสนกันทั้งโลก ซึ่ง Mock ใน Test Doubles จะหมายถึง การทดสอบพฤติกรรมการทำงานของสิ่งๆหนึ่ง (Behavior Verification) อย่าง Class/Function เป็นต้น ว่าทำงานตามที่เราต้องการหรือไม่</p>
<p>กล่าวคือ Mock ไม่ได้สนใจผลลัพธ์ปลายทาง แต่สนใจการทำงานของสิ่งที่ต้องการทดสอบ เช่น ส่งค่าเข้าไปใน process แล้วค่านั้นมีการเปลี่ยนแปลงไปตามที่เราคาดหวังหรือไม่</p>
<p><strong>ตัวอย่าง Mock</strong></p>
<p>ระบบ Login ที่ผมสร้างไว้ จะระบุ Username/Password ผ่าน Constructure ชื่อ Member() จากนั้นจะทำการส่งต่อไปให้ CheckAuthorize() เพื่อตรวจสอบว่าถูกต้องหรือไม่ โดยถ้า ถูกต้อง มันจะแสดงผลออกมาที่ Profile() ว่า &#8220;Welcome to member area&#8221;</p>
<p>ดังนั้น Unit Test ผมจะมีหน้าตาประมาณนี้ ซึ่งผมจะ Assert ไปสองค่าว่า มีการใช้งาน <span class="pl-en">CheckAuthorize</span>() และผลลัพธ์ที่ได้คือ &#8220;Welcome to member area&#8221;</p>
<p>View the code on <a href="https://gist.github.com/ifew/6c47ec08d633ddad7bd8bb9d38da25b4">Gist</a>.</p>
<p>จากนั้นผมสร้าง Class ชื่อ <span class="pl-en">MockAuthorize</span> จาก <span class="pl-en">IAuthorize โดยผมสร้างตัวแปร checkAuthorizeWasCalled ขึ้นมา เพื่อใช้ตรวจสอบการทำงาน</span></p>
<p>View the code on <a href="https://gist.github.com/ifew/e2e7a0a9969612da23efbf92774cc8cc">Gist</a>.</p>
<p>ซึ่งถ้าพฤติกรรมการทำงานของโค้ดผมถูกต้อง จะต้องมีขั้นตอนคือ</p>
<ol>
<li>ส่ง Username/Password ผ่าน Constructure ชื่อ Member()</li>
<li>จากนั้นส่งต่อไปให้ CheckAuthorize()</li>
<li>เปลี่ยนค่าตัวแปร checkAuthorizeWasCalled จากค่า  false เป็น true</li>
<li>โยนกลับมาที่ Profile() ให้แสดงผลลัพธ์</li>
</ol>
<p>เป็นอันว่าพฤติกรรมของ Login ของผมมีการเรียกใช้ CheckAuthorize() เพื่อตรวจสอบ Username/Password ถูกต้องนะ</p>
<p><strong>ตัวอย่าง Mock โดยการใช้ Moq4</strong></p>
<p>คราวนี้เมื่อเราเข้าใจวิธีการปกติแล้ว ลองดูตัวอย่างโค้ดของการใช้ Library ชื่อ Moq4  ซึ่งจะทำให้ง่ายขึ้น เพราะสร้างตัว Mock CheckAuthorize() ไว้ใน Unit Test ได้เลย จากนั้นผม Create Object ขึ้นมา และทำการ Verify ว่ามีการเรียกใช้ CheckAuthorize() เกิดขึ้นหรือไม่</p>
<p>View the code on <a href="https://gist.github.com/ifew/d97756b4bd402230bb8b79f883b41963">Gist</a>.</p>
<h3>4. Spy</h3>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4563 aligncenter" src="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Spy.png" alt="" width="716" height="221" srcset="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Spy.png 716w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Spy-600x185.png 600w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Spy-700x216.png 700w" sizes="auto, (max-width: 716px) 100vw, 716px" /></p>
<p>ชื่อนี้แปลตรงตัวคือ สายลับ โดยเราจะส่งสายลับไปดูว่า ระบบของเรามีการเรียกใช้ Function ที่เราต้องการทดสอบจริงๆไหม ซึ่งคล้ายกับ Mock แต่แค่ไม่ได้สนใจพฤติกรรมว่าทำอะไรไปบ้าง ต้องได้ค่าอะไร (<strong>Exclusive Behavior Verification</strong>)</p>
<p>เช่น เรามีระบบรายชื่อที่จะต้องส่งอีเมลออกไป 100 รายการ ระบบของเราจะต้องมีการเรียก Function การส่งอีเมลเป็นจำนวน 100 รอบ ตามที่ต้องการจริงๆ</p>
<p><strong>ตัวอย่าง Spy</strong></p>
<p>ผมทำการสร้าง object Member ขึ้นมาสองครั้ง โดยมันจะต้องไปเรียกใช้งาน CheckAuthorize() จำนวนสองครั้งเช่นกัน ดังนั้นผมจึงทดสอบว่ามีการเรียกใช้งานจำนวนสองครั้งจริงหรือไม่</p>
<p>View the code on <a href="https://gist.github.com/ifew/cd60ca3e959184353e3868a8ddd9c3ab">Gist</a>.</p>
<p>จากนั้นผมสร้าง Class ชื่อ <span class="pl-en">SpyAuthorize</span> จาก <span class="pl-en">IAuthorize โดยผมสร้างตัวแปร checkAuthorizeWasCalled ขึ้นมา เพื่อใช้นับจำนวนการเรียกใช้งาน</span></p>
<p>View the code on <a href="https://gist.github.com/ifew/fde5216072837155fe34ae4042eecc0c">Gist</a>.</p>
<p>สังเกตไหมครับว่า Spy มีการเพิ่มโค้ดเพื่อส่งเข้าไปสอดแนมโดยไม่จำเป็นกับการทำงานด้วย ดังนั้นถ้าเราต้องการทดสอบกับโค้ดที่จะใช้งานจริง เช่น คลาส Authorize() มันก็จะต้องเพิ่มเข้าไปในนั้นด้วย มันจึงเป็นข้อเสียอย่างหนึ่ง</p>
<h2>5. Fake</h2>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-4561 aligncenter" src="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Fake.png" alt="" width="701" height="261" srcset="https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Fake.png 701w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Fake-600x223.png 600w, https://myifew.com/wp-content/uploads/2018/07/TestDoubles-Fake-700x261.png 700w" sizes="auto, (max-width: 701px) 100vw, 701px" /></p>
<p>ชื่อก็บอกอยู่ว่าคือการโกหกหลอกลวงนะฮะ ฮ่าๆ มันคือการจำลองอะไรบางอย่างให้เสมือนจริง เหมือนมากจนเป็นข้อเสียหนึ่งที่เราอาจจะสับสนเอง โดยเหมาะกับใช้ในกรณีที่ไม่สามารถทำบน Production หรือระบบที่เราไม่ต้องการให้เกิดผลกระทบ</p>
<p>โดยกรณีนี้ถูกยกตัวอย่างขึ้นมาชัดเจน ก็คือ การใช้ In-Memory เก็บข้อมูลสำหรับทดสอบ แทนการใช้ Database จริงๆ  เป็นต้น</p>
<p>เช่น ระบบมีการสร้าง Order ไว้ใน Database แต่ไม่อยากสร้างจริงใน Database เพราะจะทำให้เลข Order ID ถูกเพิ่มจำนวนเข้าไป และก็ห้ามลบออกด้วย เพราะ Order ID จะขาดช่วง, ดังนั้นเราจะต้องสร้าง Database จำลองขึ้นมา ซึ่งถ้าให้ทุกเครื่องต้องทดสอบได้ และมีความเร็วมากๆ เราก็ต้องใช้ In-Memory นั่นเอง</p>
<p><strong>ตัวอย่าง Fake</strong></p>
<p>ผมต้องการทดสอบว่า Function ชื่อ Get_Member_Information_By_ID() สามารถส่ง Member ID เข้าไป และคืนค่าจาก Database เป็นไปตามที่ผมกำหนดหรือไม่ ดังนั้น แทนที่ผมจะเรียกใช้ Database จริง ผมจึงสร้าง In-Memory Database ขึ้นมาก้อนหนึ่งชื่อ get_by_id_members และทำการใส่ข้อมูลเข้าไปก่อน จากนั้นส่ง Member ID 1 เข้าไปให้ Get_Member_Information_By_ID และทำการตรวจสอบว่า ได้ชื่อ เบอร์โทร วันเดือนปีเกิด ตามใน Database จริงๆหรือไม่</p>
<p>View the code on <a href="https://gist.github.com/ifew/372ecee0dedd227d00e2b368820bda4f">Gist</a>.</p>
<h2>สรุป</h2>
<p>Test Doubles มี 5 ประเภท ที่จะนำไปเลือกใช้ได้ตามสมควร</p>
<ol>
<li><strong>Dummy</strong>: ใช้อะไรก็ได้เพื่อ By Pass การทำงาน แทนการใช้ Dependency จริง</li>
<li><strong>Stub</strong>: กำหนดค่าบางอย่าง เพื่อให้ได้ผลลัพธ์เป็นค่าที่เราต้องการเสมอ</li>
<li><strong>Mock</strong>: เพื่อ verify ว่า process ที่เรียกใช้ มีการส่งค่าไปหา Dependency ตรงตามที่เราคาดหวังหรือเปล่า</li>
<li><strong>Fake</strong>: จำลองบางอย่างขึ้นมาเพื่อใช้แทนของจริง</li>
<li><strong>Spy</strong>: เพื่อ verify ให้แน่ชัดในการใช้ process เช่น โดนเรียกใช้ไปกี่ครั้ง หรือพารามิเตอร์อะไรถูกส่งไปบ้าง</li>
</ol>
<p>หากต้องการเขียน Unit Test เราเลี่ยงไม่ได้ที่จะต้องเรียนรู้เรื่อง Test Doubles เพื่อตัดการเกี่ยวข้องกับสิ่งต่างๆ ทรี่ไม่จำเป็นออก (Dependency) เพราะว่า เราต้องการเจาะจงทดสอบเพียงอย่างเดียวเท่านั้น อย่างอื่นที่ไม่ทดสอบ เราก็จำลองขึ้นมาโดยใช้ Dummy, Stub, Fake ตามแต่จะเลือกใช้ และเราก็ใช้ Mock, Spy เพื่อทดสอบว่าโค้ดที่เขียน มันได้ทำงานถูกต้องจริงๆใช่ไหม ซึ่งวิธีการนี้ นำไปใช้ได้กับทุกภาษาโปรแกรม</p>
<p>และถ้าสังเกตให้ดี จะใช้ Test Double ได้ จำเป็นต้องรู้เรื่อง Unit Test,  Dependency-Injection และการเขียนโปรแกรมแบบ  Object-Oriented ด้วยนะฮะ ไม่เช่นนั้น เข้าใจยากเลยทีเดียว</p>
<h3>อ้างอิง</h3>
<ul>
<li><a href="https://www.javacodegeeks.com/2015/11/test-doubles-mocks-dummies-and-stubs.html" target="_blank" rel="noopener">Test Doubles: mocks, dummies and stubs </a> &lt;&lt; ตัวอย่างน่าสนใจ เข้าใจง่ายดี</li>
<li><a href="https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da" target="_blank" rel="noopener">Test Doubles — Fakes, Mocks and Stubs.</a> &lt;&lt; แนะนำให้อ่านครับ สรุปเข้าใจง่าย ตัวอย่างดี อ่านง่าย</li>
<li><a href="https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html" target="_blank" rel="noopener">The Little Mocker</a> &lt;&lt; แนะนำให้อ่านครับ  อ่านง่าย ตัวอย่างเข้าใจง่ายมาก บทความนี้เขียนจากพื้นฐานเว็บนี้</li>
<li><a href="https://martinfowler.com/articles/mocksArentStubs.html" target="_blank" rel="noopener">Mocks Aren&#8217;t Stubs</a> &lt;&lt; แนะนำให้อ่านครับ อธิบายละเอียดเลย</li>
<li><a href="https://adamcod.es/2014/05/15/test-doubles-mock-vs-stub.html" target="_blank" rel="noopener">Understanding Test Doubles (Mock vs Stub)</a> &lt;&lt; แนะนำให้อ่านครับ สรุปสั้นๆ ตัวอย่างดี อ่านง่าย</li>
<li><a href="http://www.somkiat.cc/test-double-mock-stub-and-dummy/" target="_blank" rel="noopener">มาดูกันว่า Mock, Stub และ Dummy แตกต่างกันอย่างไร ?</a> &lt;&lt; ผมอ้างอิงการแปลภาษาไทยจากเว็บพี่ปุ๋ย</li>
<li>และสุดท้าย กราบขอบพระคุณ<a href="https://www.facebook.com/boyone" target="_blank" rel="noopener">พี่บอย</a> Elder แห่งสยามชำนาญกิจ ที่ช่วยอ่านรีวิวและแนะนำมา ณ ที่นี้ครัช</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://myifew.com/4556/how-to-skip-dependency-in-unit-test-by-test-doubles/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
