Test Driven Development for Infra????

Tanat Lokejaroenlarb
NonTechCompany
Published in
3 min readNov 1, 2020

หลายๆคนอาจจะคุ้นเคยกับ concept ของการเขียน test เพื่อทดสอบความถูกต้องของ code และ Test drivent developement (TDD) ที่ให้เรา design code ของเราโดยเริ่มจากการเขียน test (เขียน test ก่อนที่จะเขียน code จริงๆ) เพื่อเพิ่ม quality เเละ coverage ของ code ของเรา

หลังๆมานี้ผมก็ได้มาจับงานทางด้าน DevOps มากขึ้น แต่ทว่า ตั้งแต่เริ่มเขียน Infrastrucutre code มาหลายเดือน ผมกลับไม่เคยเขียน test เลย ทั้งๆที่เจ้า Infrastructure มันก็คือ code เหมือนกันนั่นแหละ เรามี logic ในการ transform input ต่างๆที่มีโอกาสที่จะผิดพลาดได้เช่นกัน

ดังนั้นวันนี้ผมจะพาตัวเองกลับมาทำ best practice ที่ทำกับ application code มาตลอดมาทำกับ Infra code บ้าง

ในส่วนของ Tool ด้วยที่เราใช้ Terraform ในการจัดการ Azure infrastructure ผมก็ไป research มาว่ามี tool อะไรบ้างที่ใช้เขียน test สำหรับ infra ก็เจอมาหลายตัว แต่ตัวที่สนใจคือ “TerraTest” เนื่องจากมีผู้สร้างเดียวกับ Terragrunt ที่เราใช้อยู่ และมันเขียนด้วย Go! (เขียนไม่เป็น แต่อยากเรียนพอดี)

มาเริ่มกันเลย โดยโจทย์คือเราอยากจะทำการสร้างอะไรง่ายๆเช่น Azure Container Registry ขึ้นมาด้วย Terraform

Set up

(ตามไปดูวิธีการ set เต็มๆที่ https://terratest.gruntwork.io/docs/getting-started/quick-start/#requirements) เนื่องจากผมจะตัด part จุกจิกรวมไปถึงการ set up subscription ของ azure ออก แล้ว focus ไปที่ concept ของการ test

ในส่วนของ code เราจะสร้างไฟล์ main.tf ขึ้นมาโดยใส่ไว้แค่ provider เพื่อให้รันได้

จากนั้น สร้าง Folder ชื่อ test ขึ้นมาที่ๆ main.tf อยู่ และทำการสร้าง test file ขึ้นมาโดยลอก Template มาจาก official web ของ Terratest ซึ่งจะเป็น convention ของ go test คือชื่อไฟล์ลงท้ายด้วย _test.go

ซึ่ง content ของมันก็จะมีรูปแบบของ Arrange Act Assert โดยเจ้า Terratest จะทำการอ่าน Terraform template ตาม path ที่เราระบุ เเละสร้าง Infrastructure โดยการรัน terraform init และ apply โดยเรามี option ที่จะให้มันอ่าน variable file เพื่อใช้ในการสร้าง หรือสามารถระบุ variable เข้าไปตรงๆเลยก็ได้

หลังจากนั้นใน part Assert Terratest จะอ่าน output จากการ run เพื่อให้เราสามารถนำมาเทียบกับ state ที่เรา expect

จบด้วยการรัน Terrafrom destroy เพื่อลบ infra ที่ถูกสร้างระหว่าง test (มันสร้างของขึ้นมาจริงๆนะ ดังนั้นต้องลบด้วย ซึ่งการที่มันสร้างของขึ้นมาจริงๆนั้น Terratest จึงเป็น Integration test ไม่ใช่ unit test นั่นเอง)

Arrange Act Assert convention

จากนั้นทำการ cd เข้าไปใน test แล้วรัน command go mod init <name>/<module-name> เพื่อบอกว่า folder นี้คือ go module

หลังจากนั้นเราสามารถลองรัน go test -v เพื่อ execute test ได้เเล้ว

ผลลัพธ์จะเห็นได้ว่าค่า actual ไม่เป็นไปตามที่ expect test case นี้จึง fail

เขียน Test

เมื่อ Set up เสร็จเเล้วเราก็จะเริ่มเขียน test case

ในที่นี้ผมต้องการ Azure Container Registry ที่ชื่อ unitTestACR สร้างใน Southeast Asia โดยเก็บไว้ที่ test-rg Resource group เเละมี SKU เป็น Standard

ลอง run test อีกรอบก็จะเจอ failure มากมายในทุก assert ที่เราใส่ เพราะเรายังไม่ได้เริ่มเขียน code infra จริงๆ (TDD concept)

เขียน code

เริ่ม implement code ง่ายๆเพื่อสร้าง Azure Container Registry ตามที่ต้องการ

implement code ตามที่ต้องการ

เมื่อลองรัน Test อีกครั้ง ระหว่างที่ run test เมื่อเราเข้าไปดู Infrastructure จริงๆจะเห็นว่า

infra ถูกสร้างอย่างถูกต้องตาม spec

มี Azure Container Registry ถูกสร้างขึ้นมาอย่างถูกต้องตาม spec แล้วแต่วา

test ของเรายังคง fail อยู่ เนื่องจากว่าเรายังไม่ได้เขียน output file ขึ้นมาเพื่อเก็บ output ทำให้ Terraform ยังไม่รู้จัก value ของสิ่งที่ถูกสร้าง

เขียน Output

output.tf ทำการเก็บค่าที่สนใจจากการถูกสร้าง

เมื่อเขียน output เสร็จเเล้วทำการรัน test อีกครั้งพบว่ามี test case สุดท้ายที่ยัง fail เพราะผม expect ชื่อ location ไม่ตรงตาม format จริงที่ Azure สร้าง

เมื่อแก้ format เเละ run test อีกครั้ง

ในที่สุดก็ผ่านเเล้ว

การนำไปประยุกต์ใช้

การนำ Terratest ไปใช้สามารถเริ่มจากการใช้ใน development work ทั่วไปตอนที่เราเขียน Infra code รวมไปถึงการนำเอาไปรันใน CI/CD pipeline ของ Infrastructure ระบบเรา เพื่อ ensure ว่าทุกครั้งที่เราสร้าง infra ขึ้นมาใหม่เราได้ของตาม spec ที่ต้องการจริงๆ

ในโลกแห่งความเป็นจริง code ที่เราเขียนก็จะมีความ complex มากกว่าตัวอย่างข้างต้น ไม่ตรงไปตรงมาแบบนี้ ซึ่งก็จะมี technique อื่นๆที่ใช้ช่วยในการ set up test case ที่ต้องศึกษากันต่อไป

ขอบคุณครับ

( source code อยู่ที่ https://github.com/InsomniaCoder/terraform-tdd)

--

--