How to Integration Testing with Docker Sandbox Technique

Pawut Jingjit
5 min readDec 17, 2022

--

บทความนี้จะว่าด้วยการใช้ Docker Sandbox Technique เพื่อทำ Integration Testing โดยภาษาที่ใช้ในบทความนี้จะเป็น GO (แต่ถึงเพื่อนๆใช้ภาษาอื่น ก็ยังคง Follow ตามบทความนี้ได้อยู่ดี)

อาจจะไม่มีทฤษฏีมากนัก เพื่อนๆน่าจะจำเป็นต้องศึกษามาก่อนว่า Docker คืออะไร ทำงานอย่างไร (แต่จะ Follow ตามบทความไปเลยก็น่าจะรันตามผ่านได้อยู่ครับ)

โดย Pipeline ของบทความนี้จะเป็น

  • สร้าง Test Function ง่ายๆ อย่าง Test Sum
  • สร้าง Test Container จาก Dockerfile เพื่อทดสอบ Test Sum
  • สร้าง Docker Compose ที่มี 2 Service คือ Test_Sandbox (ที่ใช้ Dockerfile ข้างบน ) และ DB
  • ทดสอบ Docker Compose ที่เราสร้าง ว่าสามารถใช้งานได้จริงไหม โดยลองต่อ Database ดูจริงๆ ผ่าน DB.go , DB_test.go

เพื่อนๆอย่าลืมติดตั้ง Docker มาก่อนนะครับ

Sum & Test Sum

เริ่มจาก Test Function Basic อย่าง Sum
อย่าลืม go init ก่อนนะ
go test -v ./…

เพื่อนๆคงทราบดีอยู่แล้ว ว่าเราสามารถ Run Test ได้ผ่าน “go test” ซึ่งแน่นอนว่า Test Sum นี้สามารถทำงานได้อย่างถูกต้อง

ปัญหาคือ ในงานจริง เราอาจจำเป็นต้องต่อ Database , อาจต้องต่อกับ network ซึ่ง Unit Test สามารถแก้ปัญหาดังกล่าวได้

แต่ท้ายที่สุดแล้ว ระบบย่อมต้องการ “Integration Testing” เพื่อทดสอบว่า ทั้งระบบจะสามารถทำงานพร้อมกันได้ไหม

หนึ่งในวิธีที่นิยมในการทำ “Integration Testing” คือ “Docker Sandbox” โดยการใช้ “Docker Compose” เพื่อสร้าง Enviroment สำหรับการ Test เลย

ซึ่งมีข้อดีคือ

  • ควบคุม Enviroment ได้ง่าย ไม่เกิดปัญหา “แต่ในเครื่อง Local มันขึ้น Pass นะ”
  • สามารถเคลื่อนย้ายได้ง่าย ระหว่างเครื่อง Local , Server รวมไปถึงการทำ CI (continuous integration) ที่ยอมให้ใช้ Docker-Compose ได้เลย
  • อันนี้เฉพาะเจ้าของบทความ เวลาที่ดูท่าไม่ดี (อย่าง Database เริ่มมีปัญหา) เราสามารถย้อนทุกอย่างสู่จุดเริ่มต้นได้ในคำสั่งเดียว

Dockerfile — Docker Image — Docker Container

ทบทวนความรู้คล่าวๆเกี่ยวกับ Docker กันก่อน

Docker File : Text File ที่ใช้ในการสร้าง Docker Image ผ่านคำสั่ง “docker build”

Docker Image : Stand-alone , executable package ที่มีทุกอย่างซึ่งจำเป็นในการ Run Program ที่ระบุใน Image สามารถนำไปสร้าง Container ผ่าน “docker run”

Docker Container : running instance ของ Docker Image

Docker

เริ่มจาก Docker ที่ Run ง่ายๆก่อน

FROM golang:1.19 -alpine :ใช้ images golang:1.19-alpine จาก docker hub

WORKDIR /app : กำหนด WORKDIR ที่เราใช้งาน (นั่นหมายถึง CMD,COPY จะทำงานที่ DIR นี้)

COPY . . : Copy ทุกไฟล์จาก . (DIR ที่รัน Dockerfile) ไปยัง. (/app)

CMD CGO_ENABLED=0 go test -v ./… : หลังจาก Docker Container ถูกสร้าง จะรัน go test -v ./… (CGO ต้องใส่ เพราะ golang:1.19 -alpine ถ้าเป็น Ver อื่น จะไม่ต้องใช้)

docker build -t test_sum . 

docker build :สร้าง images จาก Dockerfile

-t test_sum :ชื่อและtagของ images ที่จะสร้าง ปกติจะใช้เป็น name:tag (ในที่นี้ใช้ name เป็น test_sum)

. : สร้างจาก Dockerfile ใน Directory ที่รัน cmd นี้

docker images
หลังจาก build เสร็จแล้ว สามารถดู images ที่เราสร้างจาก “docker images”
docker run test_sum
หลังจากได้ images มาแล้ว สามารถ run ได้ผ่าน docker run แน่นอนว่าทำงานได้เช่นเดียวกับบน Local

ในตอนนี้ เรายังไม่ได้มี Database ใน Dockerfile นี้ แต่เพื่อนๆคงจินตนาการออกว่า คงจะลำบากมาก เพราะเราจำเป็นต้อง Build — Run Dockerfile Database ก่อน Dockerfile Service

Script คำสั่งที่จะบอกว่าจะ Build — Run Container ไหนบ้าง อันไหนก่อนหลัง รวมไปถึงการกำหนด PORT , Network ของ Container เหล่านั้น มีชื่อว่า Docker Compose

อนึ่ง Dockerfile นี้ยังมีปัญหาอยู่ประการหนึ่ง คือ เมื่อเราใช้ COPY File จาก Host ไป Image หมายความว่า ทุกครั้งที่เราเปลี่ยนแปลง Code เราจึงต้อง Build ใหม่ทุกรอบ ซึ่งเราจะแก้ปัญหานี้กันใน Docker Compose เช่นกัน

Docker Compose

มีปัญหากับการจัดการ Docker หลายๆ Docker ใช่ไหม Docker Compose ย่อมเป็นคำตอบของเพื่อนๆ
Dockerfile เดิมๆจากข้างบน เพิ่มเติมคือลบ COPY ออก

version 3.3 : version ของ docker-compose

services : บอกว่ามี service อะไรบ้าง ในตัวอย่างคือ มี test_sandbox และ db

build : Option สำหรับการ build images ในตัวอย่าง ประกอบด้วย context , dockerfile

context : Dir ไหนใน host ที่จะทำงาน (อย่างเช่น /app หมายความว่า ทุกอย่างที่สนใจ รวมถึง Dockerfile อยู่ใน /app )

Dockerfile : ชื่อของ Dockerfile ที่จะให้ Build — Run

volumes : ใช้แทน COPY ที่ลบออกไป คำสั่งนี้จะเชื่อม . (Dir ปัจจุบันที่รัน Compose) กับ /app ใน test_sandbox container ทำให้ container เห็น file ที่เราต้องการจะ test โดยไม่ต้อง COPY เข้าไปใน container

depends_on :db : container นี้ จะถูก Build & Run หลังจากที่ db container ถูก Run แล้วเท่านั้น

image : ใช้ image postgres:14.1-alpine จาก docker-hub (เสมือน FROM ใน Dockerfile)

enviroment : กำหนด enviroment ของ database container นี้ ในบทความนี้จะใช้เพียงกำหนด id , pass และชื่อ database

port : Map port ของ host และ container (hostport : containerport)

expose : กำหนดให้ container เปิด port ไหนบ้าง(ถ้าไม่เปิด 5432 service container จะไม่สามารถเรียก database ได้)

volumes : Map ./db ของ host กับ /docker-entrypoint-initdb.d

ที่น่าสนใจคือ Volumes ของ db container คือ เมื่อ Docker Images ถูกสร้างขึ้น จะรัน Script (ในที่นี้คือ file .sql , .js ) ที่อยู่ใน docker-entrypoint-initdb.d ก่อน

./db/01-init.sql เป็น SQL Script สำหรับ Init Database
เมื่อได้ docker-compose.yml แล้ว มาตรวจสอบกันดูว่า สามารถใช้เป็น sandbox ได้จริงไหม ด้วยการลองต่อ database ดูเลย

root:root : เป็น id และ password ที่ถูกตั้งใน docker-compose.yml

db : เป็นชื่อของ container ที่ตั้งใน docker-compose.yml (สามารถละการใส่ port ได้ เพราะใช้ port default)

ยังจำได้ไหม ว่าเราใช้คำสั่ง test go -v ./… เพื่อที่จะรู้ว่าต่อ db.go ทำงานได้จริงไหม ก็ต้องสร้าง db_test.go มาทดสอบ
เมื่อทุกอย่างครบแล้ว เช็ค DIR ถูกไหม , file วางถูกตำแหน่งไหม

ถึงตอนนี้ Sandbox ของเราก็ควรจะสามารถทำงานได้เลย เพื่อนๆสามารถ Run Docker Sandbox ได้เลย

docker-compose -f docker-compose.yml up — exit-code-from test_sandbox

-f docker-compose.yml : เป็นการบอกว่า file compose ชื่อว่า docker-compose.yml (ตอนทำงานจริง มันจะแยก docker-compose.yml , docker-compose-test.yml)

— exit-code-from test_sandbox : ถ้าไม่มี args นี้ ตอนที่ test_sandbox ทำงานเสร็จ test_sandbox container จะปิดตัวลง แต่ db จะไม่ปิดตาม แน่นอนว่า เราสร้าง sandbox เราย่อมไม่ต้องการให้ db_sandbox นี้ทำงานเบื้องหลังในเครื่องเราต่อแน่ๆ จึงสั่งว่า เมื่อได้รับ exit-code จาก test_sandbox ทุก container ใน compose นี้จะปิดลง

ผลลัพท์จากการรัน docker up ดังกล่าว ทุกอย่างทำงานได้อย่างถูกต้อง ทุก container ปิดตัวลงอย่างสวยงาม
docker-compose -f docker-compose.yml down

สำหรับกรณีใดๆก็ตามที่ container ไม่ได้ปิดตัวลง สามารถรันคำสั่งนี้เพื่อปิดทุกอย่างใน compose ได้

Warning

สำหรับเพื่อนๆที่ไม่คุ้นเคยกับการใช้ docker ผมขอแนะนำให้เพื่อนๆ Check อยู่เสมอว่า แก๊งค์ Docker Images ในกินพื้นที่ในเครื่องเพื่อนๆถึงขนาดไหนแล้ว

สำหรับคนที่ใช้แบบ UI จะเห็นได้อย่างชัดเจน ถ้าใช้บน terminal สามารถตรวจสอบได้ผ่าน “docker images” เจ้าของบทความเคยมีตัวที่ Build มาเป็น 20–30 อันเลย ใช้พื้นที่อันละ 300mb. เลยนะ

บทส่งท้าย

https://www.kbtgkampus.tech/classnest/go-software-engineering-bootcamp

อยากจะบอกว่า บทความนี้ เป็นหนึ่งในบทความที่ออกนอก Comfort Zone ของเจ้าของบทความมากๆเลย (ปกติเป็นชาว Dev อย่างเดียว DevOps อะไรนี่ ไม่เคยมี) ถ้ามีอะไรผิดพลาดในคำอธิบาย หรือเนื้อหาขาดเกินเช่นใด ก็ขออภัยมาในโอกาสนี้ด้วยครับ

บทความนี้เป็นเนื้อหาของบทเรียน Docker-compose Sandbox Testing Technique ใน KBTG Kampus ที่สอนโดย อาจารย์ภูริเดช สุดสี หรือ อ.เอ๊าะ ครับ

ซึ่งเป็นเพียงเนื้อหาจาก 1 บทในค่าย จากประมาณ 50 บทครับ // เจ้าของบทความย่อคำสั่งให้เหลือแค่ที่คิดว่าจำเป็นจริงๆด้วยนะครับ เนื้อหาจริง Code ทรงพลังมาก

เทียบกับอ.หน่องแล้ว คาบเรียนของ อ.เอ๊าะ จะเป็นสาระเน้นๆ มุกตลกอาจจะน้อยหน่อยครับ ใครชอบเรียนแบบเข้มๆแบบมหาวิทยาลัย น่าจะถูกใจครับ

อยากจะฝากเพื่อนๆไว้เช่นเดิมครับ ว่า เป็นค่ายที่ได้ความรู้เยอะมากครับ (คิดสภาพว่า หนึ่งบทแบบย่อยังยาวขนาดนี้ 50 บท จะโหดขนาดไหน) ในค่ายต่อๆไป ถ้าเพื่อนๆมีโอกาสก็อยากจะให้ลองสมัคร KBTG Kampus ดูครับ

สุดท้ายก็ขอขอบคุณเพื่อนๆที่อ่านบทความนี้จนจบ อาจารย์เอ๊าะที่สอนวิชานี้ รวมไปถึงทาง KBTG ที่มอบโอกาสดีๆนี้ให้ครับ

สำหรับเพื่อนๆที่ไม่อยากพิมพ์เอง สามารถดู Code ในบทความนี้ได้ที่ Github ของเจ้าของบทความครับ ถ้าเจ้าของบทความมั่วประการใด อยากจะสอนมวย สามารถคุยกันเล่นๆได้ที่ Page นะครับ

--

--