ในฐานะนักพัฒนา Ruby/Rails เรารักการเขียนแบบทดสอบ การทดสอบเป็นส่วนสำคัญของการพัฒนาซอฟต์แวร์ การทดสอบที่ดีช่วยให้เราเขียนโค้ดคุณภาพสูงได้ พวกเขาเพิ่มมูลค่าให้กับกระบวนการพัฒนาโดยรวม แต่ถ้าเราจัดการการทดสอบไม่ดี การทดสอบนั้นอาจทำให้เราช้าลงได้ ต่อไปนี้คืออาการบางอย่างของการทดสอบที่ได้รับการจัดการอย่างไม่เหมาะสม:
- การดำเนินการทดสอบใช้เวลานาน
- การทดสอบไม่น่าเชื่อถือและล้มเหลวแบบสุ่ม
- การทดสอบทำงานแตกต่างกันในเครื่องที่ต่างกัน
- การเรียกใช้ชุดทดสอบทั้งหมดจะทำให้ CI ของคุณช้าลง
ในบล็อกโพสต์นี้ เราจะอธิบายวิธีการใช้คอนเทนเนอร์เพื่อให้การทดสอบมีความน่าเชื่อถือ ซึ่งเป็นประโยชน์ในการนำเสนอซอฟต์แวร์คุณภาพสูง
การทดสอบการรวมและการทดสอบหน่วย
ก่อนที่เราจะข้ามไปที่หัวข้อของคอนเทนเนอร์ ให้เราเข้าใจก่อนและทำความเข้าใจเกี่ยวกับความแตกต่างระหว่างการทดสอบหน่วยและการรวมเข้าด้วยกันก่อน
การทดสอบหน่วยเป็นวิธีทดสอบบล็อกของโค้ดแบบแยกส่วน ช่วยทดสอบการทำงานของกลุ่มโค้ด แต่ไม่ใช่การขึ้นต่อกัน ตัวอย่างเช่น นี่เป็นวิธีง่ายๆ:
def sum(a, b)
a + b
end
นี่คือฟังก์ชันที่รวมตัวเลขสองตัว ซึ่งง่ายมาก การทดสอบฟังก์ชันนี้จะง่ายมาก แต่ถ้าฟังก์ชันนี้ต้องพึ่งพาบริการภายนอกล่ะ วิธีนี้จะต้องเขียนลงฐานข้อมูลเมื่อผลรวมเสร็จสมบูรณ์
def sum!(a, b)
c = a + b
Sum.create(value: c)
end
ในบล็อคของโค้ดนี้ ค่าจะถูกเพิ่มและบันทึกลงในฐานข้อมูล เพื่อทดสอบบล็อคของโค้ดนี้ เราจำเป็นต้องมีเซิร์ฟเวอร์ฐานข้อมูลที่ทำงานอยู่ หากเราทดสอบสิ่งนี้กับเซิร์ฟเวอร์ฐานข้อมูลที่ทำงานอยู่ จะไม่มีการทดสอบหน่วยอีกต่อไป สิ่งนี้ต้องการการพึ่งพาจากภายนอก และเราจะทดสอบการเชื่อมต่อฐานข้อมูลด้วยและบันทึกว่าบันทึกในฐานข้อมูลหรือไม่ ในกรณีนี้ การทดสอบนี้เป็นการทดสอบการรวม
การทดสอบหน่วยสามารถการทดสอบหน่วยเดียว (จำลอง/สลบการพึ่งพาทั้งหมด) หรือ การทดสอบหน่วยที่เข้ากับคนได้ (อนุญาตให้พูดคุยกับการพึ่งพาอื่น ๆ ) ผู้คนได้เสนอคำจำกัดความต่าง ๆ ของการทดสอบหน่วย ในบริบทของบล็อกนี้ ประเภทของการทดสอบหน่วยที่เรากำลังพูดถึงคือการทดสอบหน่วยเดี่ยว
การลบการพึ่งพาภายนอกด้วยการจำลองช่วยให้การทดสอบทำงานเร็วขึ้น ขณะใช้การทดสอบหน่วย เราละเว้นการพึ่งพาเหล่านี้ แต่เพื่อให้แน่ใจว่าแอปพลิเคชันทำงานตามที่คาดไว้ การทดสอบการขึ้นต่อกันมีความสำคัญเท่าเทียมกัน การทดสอบที่เราเขียนเพื่อทดสอบการพึ่งพาภายนอกเหล่านี้ เช่น ฐานข้อมูล การเรียกใช้เครือข่าย และไฟล์ คือการทดสอบการผสานรวม .
เช่นเดียวกับการทดสอบหน่วย ผู้คนได้เสนอคำจำกัดความต่างๆ ของการทดสอบการรวมเข้าด้วยกัน อย่างไรก็ตาม แนวคิดพื้นฐานก็เหมือนกัน ความแตกต่างอยู่ในขอบเขตเท่านั้น ฟังก์ชันขนาดเล็กที่เขียนลงในฐานข้อมูลอาจเป็นการทดสอบการรวม ฟังก์ชันการทำงานที่กว้างขึ้นซึ่งเชื่อมต่อหลายส่วนของระบบยังเป็นการทดสอบการรวมระบบอีกด้วย การทดสอบที่เรากำลังพูดถึงในที่นี้เน้นไปที่ส่วนที่แคบกว่า ยิ่งการทดสอบแคบลงเท่าใด เราก็ยิ่งต้องเขียนการทดสอบมากขึ้นเท่านั้น อย่างไรก็ตาม ด้วยการทดสอบที่กว้างขึ้น จำนวนการทดสอบที่จำเป็นจะลดลง
ขณะรันการทดสอบการรวม เราจำเป็นต้องรันแอปพลิเคชันและการอ้างอิง เช่น ฐานข้อมูลและบริการภายนอก การจัดการการพึ่งพาเหล่านี้อาจเป็นเรื่องที่ท้าทาย
พื้นฐานของนักเทียบท่า
Docker เป็นเทคโนโลยีที่ช่วยนามธรรมแอปพลิเคชันจากสภาพแวดล้อม (ระบบปฏิบัติการ) และเรียกใช้โดยแยกกับโฮสต์ สิ่งนี้คล้ายกับเครื่องเสมือน แต่เร็วกว่าและมีประสิทธิภาพมากกว่า Docker ช่วยให้เราจัดแพ็คเกจโค้ดแอปพลิเคชันและการอ้างอิงของมันไว้ในคอนเทนเนอร์เดียว สิ่งนี้ช่วยทำให้โค้ดพกพาได้ และหมายความว่าเราสามารถมีอิมเมจ Docker เดียวกันทำงานในระหว่างการพัฒนา การทดสอบ และการผลิต สิ่งนี้จะช่วยให้มั่นใจถึงความสอดคล้องของสภาพแวดล้อมที่แอปพลิเคชันทำงาน เนื่องจากไม่ได้ขึ้นอยู่กับเซิร์ฟเวอร์โฮสต์ มันทำให้พฤติกรรมของแอปพลิเคชันสามารถคาดเดาได้ในทุกสภาพแวดล้อม Docker ช่วยให้นักพัฒนาสามารถเรียกใช้และจัดการแอปพลิเคชันและสภาพแวดล้อม รวมถึงการสร้าง การรัน การปรับใช้ และการอัปเดตโดยใช้คำสั่งง่ายๆ
ให้เราเข้าใจคำศัพท์ที่สำคัญบางคำ:
-
Dockerfile - Dockerfile เป็นเพียงไฟล์ที่เรากำหนดการอ้างอิงที่จำเป็นสำหรับแอปพลิเคชัน เราสามารถระบุระบบปฏิบัติการที่ต้องการ ไลบรารีไคลเอนต์ฐานข้อมูล หรือแพ็คเกจใดๆ ที่แอปพลิเคชันต้องการ ที่นี่ เรายังกำหนดคำสั่งที่จำเป็นสำหรับการเรียกใช้แอปพลิเคชัน Dockerfile เริ่มจากอิมเมจพื้นฐาน อิมเมจพื้นฐานเหล่านี้อาจเป็นระบบปฏิบัติการหรือภาษาการเขียนโปรแกรม
-
รูปภาพ - รูปภาพเป็นพิมพ์เขียวที่ระบุขั้นตอนที่จำเป็นสำหรับคอนเทนเนอร์ในการทำงาน รูปภาพถูกสร้างขึ้นด้วยเลเยอร์ต่างๆ ทุกขั้นตอนที่กำหนดไว้ใน Dockerfile คือเลเยอร์ภายในรูปภาพ ซึ่งจะช่วยเพิ่มความสามารถในการนำกลับมาใช้ใหม่และช่วยในการแคชเลเยอร์กลางเพื่อสร้างภาพได้เร็วขึ้น
-
คอนเทนเนอร์ - คอนเทนเนอร์เป็นภาพจริง สิ่งเหล่านี้สามารถอยู่ในสถานะหยุดได้เช่นกัน คอนเทนเนอร์สามารถเริ่มต้นได้จากอิมเมจนักเทียบท่า ภาพนักเทียบท่าเดียวสามารถเริ่มคอนเทนเนอร์นักเทียบท่าได้หลายรายการ คอนเทนเนอร์เก็บข้อมูลที่ระบุโดยรูปภาพ มีชั้นคอนเทนเนอร์บาง ๆ ที่เก็บการเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นกับรูปภาพในขณะที่แอปพลิเคชันกำลังทำงานอยู่
-
นักเทียบท่าเขียน - Docker Compose เป็นเครื่องมือที่แตกต่างที่ช่วยในการจัดการคอนเทนเนอร์หลายตัว เราสามารถมีคอนเทนเนอร์หลายตัวจากอิมเมจนักเทียบท่าเดียวกัน เราอาจต้องการคอนเทนเนอร์หลายตัวจากอิมเมจนักเทียบท่าที่แตกต่างกัน และคอนเทนเนอร์เหล่านี้จำเป็นต้องโต้ตอบกัน Docker Compose จัดการคอนเทนเนอร์ มีเครื่องมืออื่นๆ ให้ใช้ แต่ในบริบทนี้ เราจะใช้เฉพาะ Docker Compose เนื่องจากความเรียบง่าย
เครื่องเสมือนอยู่บนโฮสต์ไฮเปอร์ไวเซอร์และจำเป็นต้องติดตั้งระบบปฏิบัติการแยกต่างหาก ดังนั้นจึงใช้ทรัพยากรโฮสต์มากขึ้น อย่างไรก็ตาม ด้วย Docker เราจำเป็นต้องติดตั้งเอ็นจิ้น Docker ในระบบปฏิบัติการโฮสต์เท่านั้น และ Docker จัดการทรัพยากรโฮสต์ Docker ช่วยให้ชีวิตง่ายขึ้นสำหรับทั้ง Developer และ sysops จึงเป็นที่นิยมอย่างมากในวัฒนธรรม DevOps
เหตุใดจึงต้องใช้ Docker สำหรับการทดสอบ
Docker แยกแอปพลิเคชันที่ทำงานอยู่หรือการทดสอบออกจากสภาพแวดล้อมโฮสต์โดยใช้ทรัพยากรน้อยที่สุด นี่คือข้อดีบางประการของการใช้นักเทียบท่าในการทดสอบ:
-
น้ำหนักเบา: คอนเทนเนอร์ Docker มีน้ำหนักเบา ซึ่งจะช่วยเรียกใช้คอนเทนเนอร์หลายตัวพร้อมกัน และไม่มีกระบวนการทดสอบเดียวที่ทำให้เกิดปัญหาในกระบวนการทดสอบอื่น
-
พกพา: อิมเมจ Docker นั้นพกพาได้และสามารถทำงานได้ในทุกสภาพแวดล้อม วิธีนี้ช่วยได้ใน CI เนื่องจากเรานำอิมเมจเดิมมาใช้ซ้ำเพื่อทำการทดสอบในสภาพแวดล้อมภายในและ CI ได้
-
การกำหนดเวอร์ชัน: การใช้หลายแอพพลิเคชั่นหรือการอัปเกรดการขึ้นต่อกันของเราต้องอาศัยเวอร์ชันต่างๆ ของการขึ้นต่อกันในเครื่องท้องถิ่นของเรา สิ่งนี้จะยากมากในการจัดการหากไม่มี Docker ด้วย Docker เราสามารถมีคอนเทนเนอร์ต่างๆ ที่ทำงานด้วยเวอร์ชันอื่น เรายังทำการทดสอบในเวอร์ชันต่างๆ และตรวจสอบสิ่งต่างๆ ได้อีกด้วย
-
การจัดการไมโครเซอร์วิสอย่างง่าย :ด้วยไมโครเซอร์วิส การทดสอบยังได้รับการเผยแพร่และซับซ้อนอีกด้วย ซึ่งหมายความว่าเราจำเป็นต้องจัดการไมโครเซอร์วิสที่ขึ้นต่อกัน Docker และ Docker Compose ช่วยในการจัดการการขึ้นต่อกันของบริการเหล่านี้
ข้อกำหนดเบื้องต้น
หากต้องการลองใช้งาน ให้ตรวจสอบว่าตั้งค่าต่อไปนี้แล้ว:
- นักเทียบท่า
- นักเทียบท่าเขียน
- Ruby 2.7 (ไม่บังคับ เนื่องจากเราจะเพิ่ม Ruby ลงในคอนเทนเนอร์ Docker แต่จะมีประโยชน์สำหรับการทดสอบ)
- ราง (แนะนำ 5.1+)
เชื่อมต่อแอป
ให้เราเริ่มต้นด้วยการสร้างแอป Rails แบบง่าย ๆ และจัดคอนเทนเนอร์ ฉันกำลังเริ่มต้นด้วยแอป Rails ใหม่ คุณยังสามารถบรรจุแอป Rails ที่มีอยู่ได้
rails new calculator
สร้างไฟล์ชื่อ Dockerfile
โดยมีเนื้อหาดังต่อไปนี้:
FROM ruby:2.7
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update -qq && apt-get install -y nodejs yarn
RUN mkdir /calculator
WORKDIR /calculator
COPY Gemfile /calculator/Gemfile
COPY Gemfile.lock /calculator/Gemfile.lock
RUN bundle install
COPY package.json /calculator/package.json
COPY yarn.lock /calculator/yarn.lock
RUN yarn install --check-files
COPY . /calculator
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
Dockerfile ที่เรากำหนดมีอิมเมจฐาน Ruby 2.7 เราต้องการ NodeJS และ Yarn ดังนั้นขั้นตอนต่อไปคือการติดตั้ง เมื่อติดตั้งแล้ว เราจะเรียกใช้ bundle install
และ yarn install
. สุดท้าย เราคัดลอกโค้ดทั้งหมดและเรียกใช้เซิร์ฟเวอร์ Rails โดยส่งออกพอร์ต 3000 นี่เป็น Dockerfile ที่ง่ายมาก ด้วยเหตุนี้ แอป Rails ของเราจึงถูกเทียบท่า
ตอนนี้ เราสามารถสร้างและเรียกใช้อิมเมจนักเทียบท่าเพื่อให้แน่ใจว่ามันทำงานอยู่
สร้างภาพนักเทียบท่า:
docker build . -t calculator
เรียกใช้คอนเทนเนอร์:
docker run -p 3000:3000 calculator
สร้างแบบจำลองด้วยลอจิกการคำนวณ
ให้เราสร้างแบบจำลองที่เรียกว่าการคำนวณ ซึ่งเราสามารถเพิ่มตรรกะการคำนวณของเราได้ เราจะใช้โมเดลนี้เพื่อเขียนตรรกะและการทดสอบสำหรับพวกเขา เราสามารถดูว่าเราสามารถจัดการการขึ้นต่อกันของการทดสอบเหล่านี้ได้อย่างไรโดยใช้ Docker Compose
ขณะนี้เรากำลังเรียกใช้ Rails กับ SQLite ให้เราเปลี่ยนเป็น Postgres แทน SQLite สามารถทำได้ง่ายๆ ด้วยคำสั่งต่อไปนี้:
rails db:system:change --to=postgresql
สร้างแบบจำลองการคำนวณ:
bundle exec rails generate model calculation
เพิ่มบางฟิลด์ในสคีมาการคำนวณ:
# db/migrate/create_calculation.rb
class CreateCalculations < ActiveRecord::Migration[6.0]
def change
create_table :calculations do |t|
t.integer :x
t.integer :y
t.integer :result
t.timestamps
end
end
end
ต่อไป ให้ลองเรียกใช้ฐานข้อมูลการย้ายข้อมูลภายในคอนเทนเนอร์:
docker-compose exec web rails db:migrate
เนื่องจากฐานข้อมูลไม่ได้ทำงานอยู่ภายในคอนเทนเนอร์ การเรียกใช้คำสั่งนี้จะทำให้เกิดข้อผิดพลาด
rake aborted!
PG::ConnectionBad: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
เราสามารถเรียกใช้ภาพ Postgres เป็นอีกภาพหนึ่งและเพิ่มเครือข่ายระหว่างกัน ในการจัดการการสื่อสารคอนเทนเนอร์นี้ เราสามารถใช้ Docker Compose
การใช้ Docker Compose เพื่อจัดการคอนเทนเนอร์หลายรายการ
ด้วยคอนเทนเนอร์แอป Rails เราสามารถเพิ่มการพึ่งพาไปยังบริการโดยใช้ Docker Compose การพึ่งพาแอปพลิเคชันคือ Postgres
อิมเมจ Postgres Docker นั้นเปิดเผยต่อสาธารณะ ดังนั้นเราจึงไม่จำเป็นต้องสร้างอิมเมจดังกล่าว อย่างไรก็ตาม เราจำเป็นต้องจัดการคอนเทนเนอร์ฐานข้อมูลและคอนเทนเนอร์แอป Rails ของเรา
version: "3.0"
services:
db:
image: postgres
environment:
POSTGRES_USER: calculator
POSTGRES_PASSWORD: password
POSTGRES_DB: calculator_test
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
CALCULATOR_DATABASE_PASSWORD: password
ตอนนี้ ไปที่ database.yaml
ไฟล์และเปลี่ยนฐานข้อมูลเป็นโฮสต์อื่น โฮสต์จะเป็นชื่อที่ระบุภายในบริการในไฟล์ Docker Compose ในกรณีของเรา โฮสต์คือ db
. ตรวจสอบให้แน่ใจว่ารหัสผ่าน DB เหมือนกันใน postgres
บริการและ web
บริการ. นอกจากนี้ ให้เปลี่ยนชื่อผู้ใช้ รหัสผ่าน และชื่อฐานข้อมูลตามที่ระบุในไฟล์ Docker Compose
# database.yaml
development:
<<: *default
database: calculator_development
username: calculator
password: <%= ENV['CALCULATOR_DATABASE_PASSWORD'] %>
ตอนนี้ เราสามารถเริ่มคอนเทนเนอร์โดยใช้คำสั่งเดียว:
docker-compose up
สิ่งนี้จะเริ่มต้นทั้งฐานข้อมูลและเซิร์ฟเวอร์ Rails ของเรา เราสามารถเรียกใช้คำสั่งการโยกย้ายได้ในขณะนี้ และควรจะสามารถเชื่อมต่อกับ Postgres และเรียกใช้การย้ายข้อมูลได้
การเพิ่มการทดสอบ
เมื่อเรามีการตั้งค่าทั้งหมดพร้อมแล้ว ให้เราเขียนวิธีการง่ายๆ ภายในโมเดลและทดสอบวิธีการ:
# calculation.rb
class Calculation < ApplicationRecord
def self.sum(x, y)
result = x + y
Calculation.create(x: x, y: y, result: result)
result
end
end
วิธีผลรวมนี้ไม่เพียงเพิ่มตัวเลขเท่านั้น แต่ยังบันทึกตัวเลขลงในฐานข้อมูลด้วย ดังนั้น การทดสอบวิธีนี้ต้องใช้อินสแตนซ์ฐานข้อมูลเพื่อใช้งาน
การทดสอบนี้จะเป็นการทดสอบการรวม เนื่องจากเราต้องเชื่อมต่อกับฐานข้อมูล
เราจะใช้ Mini-test เพื่อเขียนการทดสอบ ซึ่งเป็นค่าเริ่มต้นของ Rails
# calculation_test.rb
require 'test_helper'
class CalculationTest < ActiveSupport::TestCase
test "should sum and save the data" do
result = Calculation.sum(1, 2)
c = Calculation.last
assert result, 3
assert c.result, result
end
end
ทำการทดสอบ:
docker-compose exec web rails test
ในการทดสอบข้างต้น เรากำลังทดสอบว่าวิธีผลรวมกำลังเพิ่มค่าและบันทึกค่าในฐานข้อมูลหรือไม่ ด้วย Docker Compose เรามีวิธีเชื่อมต่อกับฐานข้อมูลได้อย่างง่ายดาย
ที่นี่วิธีการขึ้นอยู่กับฐานข้อมูล การขึ้นต่อกันไม่เพียงแต่เป็นฐานข้อมูลเท่านั้น แต่ยังเป็นบริการของบุคคลที่สามที่มี REST API อีกด้วย ให้เราลองใช้บริการของบุคคลที่สามที่ให้ sum
ที่เราสามารถใช้แทนการเขียนของเราเองได้
# calculation.rb
class Calculation < ApplicationRecord
def self.sum(x, y)
# The hostname should be the same as it is specified in the docker-compose file
url = 'https://sumservice:4010/sum'
uri = URI(url)
params = { :x => x, :y => y }
uri.query = URI.encode_www_form(params)
res = Net::HTTP.get_response(uri)
throw "Error" unless res.is_a?(Net::HTTPSuccess)
result = res.body.to_i
Calculation.create(x: x, y: y, result: result)
result
end
end
เราสามารถทำสิ่งที่คล้ายคลึงกันสำหรับ REST API ที่เราทำกับฐานข้อมูลเป็นการพึ่งพา เราจำเป็นต้องตรวจสอบให้แน่ใจว่าสิ่งเหล่านี้เป็นจุดสิ้นสุดที่ถูกต้อง แต่เราไม่ต้องการโทรไปยังบริการจริงระหว่างการทดสอบ สำหรับกรณีนี้ เราสามารถสร้างการทดสอบจำลอง/สตับได้ แต่การทดสอบเหล่านี้จะไม่ใช่การทดสอบการรวมถ้าเราทำอย่างนั้น เราต้องการให้แน่ใจว่าการทดสอบนั้นเป็นจริงมากที่สุด เพื่อจุดประสงค์นี้ เราสามารถสร้างจุดสิ้นสุด API จำลอง ซึ่งเราจะเรียกใช้ในคอนเทนเนอร์ และทำการเรียกใช้บริการ API คอนเทนเนอร์จำลอง ซึ่งจะตอบสนองต่อผลลัพธ์ของเรา
ให้เราแก้ไขการทดสอบก่อน:
# calculation_test.rb
require 'test_helper'
class CalculationTest < ActiveSupport::TestCase
test "should sum and save the data" do
result = Calculation.sum(1, 2)
c = Calculation.last
# we don't need this assetion as we are deligation this responsibility to external service:
# assert result, 3 // Not needed
assert c.result, result
end
end
ที่นี่ เราได้แก้ไขกรณีทดสอบเพื่อทดสอบว่าผลลัพธ์ที่ได้จาก sum API นั้นถูกบันทึกอย่างถูกต้องในฐานข้อมูลหรือไม่ เราไม่จำเป็นต้องทดสอบว่ามูลค่าที่แท้จริงของผลรวมนั้นถูกต้องหรือไม่ เนื่องจากบริการนี้จัดการโดยบริการภายนอก และเราเชื่อถือมัน
ในการสร้างเซิร์ฟเวอร์จำลอง เราจะใช้ openAPI ให้เราสร้างคำจำกัดความง่ายๆ :
สร้างโฟลเดอร์ใหม่ในไดเร็กทอรีรากและตั้งชื่อว่า mock
. ภายในโฟลเดอร์ ให้สร้างชื่อไฟล์ api.yaml
# api.yaml
swagger: "2.0"
info:
description: "This is a sum api provider"
version: "1.0.0"
title: "API Provider"
host: "https://mock-service.com/"
basePath: "/api"
schemes:
- "https"
- "http"
paths:
/sum:
get:
produces:
- "application/json"
parameters:
- name: "x"
in: "query"
description: "x value"
required: true
type: "integer"
- name: "y"
in: "query"
description: "y value"
required: true
type: "integer"
responses:
"200":
description: "successful operation"
schema:
type: "integer"
example: 3
ในการรันเซิร์ฟเวอร์นี้ตามข้อกำหนด เราสามารถใช้ผู้ให้บริการจำลองรายใดก็ได้ ฉันจะใช้เครื่องมือที่เรียกว่าปริซึม มีภาพนักเทียบท่า ซึ่งเราสามารถเพิ่มในไฟล์ประกอบนักเทียบท่าของเรา:
version: "3.0"
services:
db:
image: postgres
environment:
POSTGRES_USER: calculator
POSTGRES_PASSWORD: password
POSTGRES_DB: calculator_test
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
- sumservice
environment:
CALCULATOR_DATABASE_PASSWORD: password
sumservice:
image: stoplight/prism:4
command: mock -h 0.0.0.0 "/tmp/api.yaml"
volumes:
- ./mock:/tmp/
ports:
- 4010:4010
init: true
ตอนนี้ มาทำการทดสอบกันและดูว่ามันทำงานตามที่คาดไว้หรือไม่:
> docker-compose exec web rails test
Finished in 0.337710s, 2.9611 runs/s, 5.9222 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
ใช่! ตอนนี้เรามีการทดสอบการทำงาน วิธีการเชื่อมต่อกับอิมเมจ Docker บริการรวมเพื่อรับผลรวมและเชื่อมต่อกับ Postgres เพื่อบันทึกข้อมูล การพึ่งพาทั้งหมดได้รับการจัดการโดย Docker Compose ดังนั้นเราจึงไม่จำเป็นต้องกังวลเกี่ยวกับการพึ่งพาที่ติดตั้งไว้ก่อนหน้านี้ การย้ายการทดสอบนี้ไปยังเครื่องใดๆ จะทำงานได้โดยไม่ต้องติดตั้งการพึ่งพาใดๆ สิ่งเดียวที่เครื่องต้องการคือนักเทียบท่า
หากแอปพลิเคชันของคุณขึ้นอยู่กับการขึ้นต่อกันอื่นๆ เช่น Redis หรือ Memcached สามารถพบได้ใน dockerhub เราสามารถเพิ่มรูปภาพเหล่านี้ลงในไฟล์ Docker Compose ได้ตามต้องการ
การทดสอบแบบขนาน
Rails 5.1.5+ รองรับการทดสอบการทำงานแบบขนาน โดยค่าเริ่มต้น การทดสอบจะทำงานแบบขนานจาก Rails 6 ตามจำนวนโปรเซสเซอร์ ขณะรันการทดสอบ จะสร้างอินสแตนซ์ฐานข้อมูลตามจำนวนการทดสอบแบบขนาน หากเรามีโปรเซสเซอร์สี่ตัว จะสร้างฐานข้อมูล "calculator_test-0", calculator_test-1, calculator_test-2 และ calculator_test-3 หากคุณไปที่ postgres cli และตรวจสอบฐานข้อมูล คุณจะเห็นสิ่งต่อไปนี้:
> \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-------------------+------------+----------+------------+------------+---------------------------
calculator_test | calculator | UTF8 | en_US.utf8 | en_US.utf8 |
calculator_test-0 | calculator | UTF8 | en_US.utf8 | en_US.utf8 |
calculator_test-1 | calculator | UTF8 | en_US.utf8 | en_US.utf8 |
calculator_test-2 | calculator | UTF8 | en_US.utf8 | en_US.utf8 |
calculator_test-3 | calculator | UTF8 | en_US.utf8 | en_US.utf8 |
ตอนนี้ แม้ว่าคุณจะไม่ได้ใช้ Rails หรือ mini-tests หรือมี Rails เวอร์ชันเก่า คุณยังคงรันการทดสอบแบบขนานถ้าคุณเขียนมันด้วย Docker Rails จะสร้างฐานข้อมูลสำหรับการทดสอบแบบขนานโดยอัตโนมัติ แต่ถ้ามีการขึ้นต่อกันอื่นๆ เช่น Redis หรือ memcached ก็อาจต้องจัดการด้วยตนเอง
เนื่องจากเรามีการจัดการการพึ่งพาทั้งหมด เราจึงสามารถเรียกใช้ Docker Compose ด้วยอาร์กิวเมนต์ที่แตกต่างกันเพื่อเรียกใช้การทดสอบแบบขนาน เพื่อจุดประสงค์นี้ เราสามารถระบุโฟลเดอร์ที่เราต้องการเรียกใช้การทดสอบแบบขนาน ดำเนินการสร้างไฟล์ Docker Compose หลายไฟล์ และเรียกใช้พร้อมกัน
การเพิ่ม Makefile ให้กับคำสั่ง Simplify
เนื่องจากเราใช้คำสั่ง Docker และ Docker Compose เพื่อเรียกใช้การทดสอบ คำสั่งจึงยาว พวกเขาสามารถจดจำและดำเนินการได้ยากในขณะที่ทำการพัฒนาแบบวันต่อวัน เพื่อให้ง่ายขึ้นเป็นคำสั่งง่ายๆ ให้เราใช้ add Makefile Makefile เป็นไฟล์ข้อความธรรมดาที่ใช้อ้างอิงการเรียกใช้คำสั่งเป้าหมาย
.PHONY: test
up:
docker-compose up
down:
docker-compose down
test:
docker-compose exec web rails test
เราได้เพิ่มคำสั่งบางอย่างใน makefile สามารถขยายได้ตามที่แอปพลิเคชันกำหนด เพิ่ม makefile ลงในไดเร็กทอรีรูทของแอปพลิเคชัน Rails เมื่อเรามี makefile นี้แล้ว เราสามารถเรียกใช้การทดสอบโดยใช้คำสั่งต่อไปนี้:
บูตคอนเทนเนอร์:
> make up
ทำการทดสอบ:
> make test
การใช้ Docker เพื่อการพัฒนาในพื้นที่
เรายังสามารถใช้ Docker ขณะพัฒนาแอปพลิเคชันได้ ซึ่งช่วยให้มั่นใจว่าแอปพลิเคชันทำงานอย่างสม่ำเสมอในการทดสอบ การพัฒนา และการผลิต ให้เราตรวจสอบสิ่งที่เราต้องเปลี่ยนเพื่อใช้คอนเทนเนอร์ด้านบนเพื่อการพัฒนาในพื้นที่
เพิ่มระดับเสียง
ขณะเรียกใช้แอปพลิเคชันในเครื่อง เราต้องการให้แอปพลิเคชันแสดงการเปลี่ยนแปลงในเซิร์ฟเวอร์ภายในเมื่อเราทำการเปลี่ยนแปลงในโค้ด ในสถานการณ์ปัจจุบัน รหัสถูกคัดลอกจากเครื่องของเราไปยัง Docker และเซิร์ฟเวอร์เริ่มทำงาน ดังนั้น หากเราทำการเปลี่ยนแปลงไฟล์ในเครื่องท้องถิ่น การเปลี่ยนแปลงจะไม่มีผลใน Docker เพื่อให้แน่ใจว่าโค้ดซิงค์อยู่เสมอ เราสามารถใช้การเมานต์ระดับเสียงได้
ในไฟล์ Docker Compose ให้เราเพิ่มโวลุ่ม:
version: "3.0"
services:
db:
image: postgres
environment:
POSTGRES_USER: calculator
POSTGRES_PASSWORD: password
POSTGRES_DB: calculator_test
web:
build: .
volumes:
- .:/calculator # Volume mounted
ports:
- "3000:3000"
depends_on:
- db
- sumservice
environment:
CALCULATOR_DATABASE_PASSWORD: password
sumservice:
image: stoplight/prism:4
command: mock -h 0.0.0.0 "/tmp/api.yaml"
volumes:
- ./mock:/tmp/
ports:
- 4010:4010
init: true
ตอนนี้ หากเราทำการเปลี่ยนแปลงใดๆ กับเซิร์ฟเวอร์ การเปลี่ยนแปลงจะมีผล ให้เราลองทำสิ่งนี้โดยสร้างคอนโทรลเลอร์และเรียกวิธีผลรวมของเรา:
# controllers/calculation_controller.rb
class CalculationController < ApplicationController
def index
result = Calculation.sum(params[:x], params[:y])
render json: {result: result}
end
end
เพิ่มเส้นทางราก:
# routes.rb
Rails.application.routes.draw do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
root "calculation#index"
end
ตอนนี้ หากเรากลับไปตรวจสอบแอปในเบราว์เซอร์ เราจะได้รับการตอบสนองที่คาดหวัง:
โปรดทราบว่าเรากำลังเริ่มต้นเซิร์ฟเวอร์โดยใช้การจำลอง sumservice
ดังนั้น ไม่ว่าเราจะให้อะไรใน URL เป็นพารามิเตอร์การสืบค้น มันก็จะส่งกลับ 2 เสมอ ซึ่งดูเหมือนจะไม่ใช่วิธีที่ดีในการพัฒนา แต่การใช้ผู้ให้บริการจำลองมีข้อดีของมัน ในการพัฒนาไมโครเซอร์วิส อาจมีบริการที่ต้องพึ่งพาอาศัยกันมากมาย ซึ่งเราจะต้องบูตเครื่องก่อนเริ่มบริการ ซึ่งจะช่วยเพิ่มผลผลิตระหว่างการพัฒนา เราไม่เกี่ยวข้องกับบริการอื่น ๆ และให้บริการที่เรากำลังพัฒนาเท่านั้น
หากเราต้องการตรวจสอบขั้นสุดท้ายว่ามันทำงานอย่างไรกับบริการจริง เราสามารถทำได้โดยการสร้างไฟล์ Docker Compose ใหม่และแทนที่ด้วยภาพจริงของบริการ หากเป็นบริการภายใน เราสามารถแทนที่ด้วยอิมเมจ Docker ที่เสถียรล่าสุดได้ ฉันขอแนะนำให้ใช้บริการจำลองแม้ในสภาพแวดล้อมการพัฒนา
.dockerignore
เมื่อแอปพลิเคชันมีขนาดใหญ่ขึ้น การขึ้นต่อกันจะทำให้บริบทการสร้าง Docker ใหญ่ขึ้น ด้วยบริบทของบิลด์ที่ใหญ่ขึ้น จึงต้องใช้เวลามากขึ้นในการสร้างอิมเมจ Docker แม้แต่การเมานต์โวลุ่มก็อาจทำให้เกิดความล่าช้าได้ด้วยขนาดของแอปพลิเคชันที่เพิ่มขึ้น ในกรณีนี้ เราสามารถเพิ่มไฟล์ที่ไม่ต้องการลงใน .dockerignore
ไฟล์เพื่อลดบริบทการสร้าง
.git*
log/*
README.md
node_modules
จุดเข้าใช้งาน
ในขณะที่เซิร์ฟเวอร์ Rails ทำงานในคอนเทนเนอร์ บางครั้งเราได้รับข้อความแสดงข้อผิดพลาดที่ระบุว่าเซิร์ฟเวอร์กำลังทำงานอยู่แล้ว เนื่องจากไฟล์ PID ไม่ได้ถูกลบเมื่อคอนเทนเนอร์ออก ในการลบข้อผิดพลาดนี้ เราสามารถเพิ่ม ENTRYPOINT ซึ่งจะลบ server.pid
ไฟล์.
FROM ruby:2.7
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update -qq && apt-get install -y nodejs yarn
RUN mkdir /calculator
WORKDIR /calculator
COPY Gemfile /calculator/Gemfile
COPY Gemfile.lock /calculator/Gemfile.lock
RUN bundle install
COPY package.json /calculator/package.json
COPY yarn.lock /calculator/yarn.lock
RUN yarn install --check-files
COPY . /calculator
EXPOSE 3000
ENTRYPOINT ./entrypoint.sh
CMD ["rails", "server", "-b", "0.0.0.0"]
สร้าง entrypoint.sh
ไฟล์:
#!/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /calculator/tmp/pids/server.pid
# Exec the container's main process (CMD command in rails file)
exec "$@"
ทำให้ปฏิบัติการได้
chmod +x entrypoint.sh
เมื่อเรารันอิมเมจ Docker เราจะไม่พบปัญหานี้
บทสรุป
การแยกความแตกต่างระหว่างการทดสอบการรวมและการทดสอบหน่วยเป็นสิ่งสำคัญมาก ขณะเขียนการทดสอบหน่วย เราสามารถจำลองการพึ่งพาภายนอกได้ เช่น ฐานข้อมูล วิธีนี้จะช่วยทดสอบโค้ดแบบแยกส่วน การรันการทดสอบการรวมระหว่างตรรกะทางธุรกิจหลักและการขึ้นต่อกันมีความสำคัญเท่าเทียมกัน การพึ่งพาอาศัยกัน เช่น ฐานข้อมูลและบริการภายนอก สามารถทดสอบในการผสานรวมได้โดยใช้ Docker และ Docker Compose เป็นการดีที่จะแยกสองสิ่งนี้ออกจากกัน เราควรจะสามารถเรียกใช้แยกกันได้ การมีการทดสอบหน่วยมากขึ้นและการทดสอบการรวมน้อยลงทำให้การทดสอบของคุณทำงานเร็วขึ้นและเชื่อถือได้มากขึ้น
ข้อดีบางประการของเวิร์กโฟลว์นี้มีดังนี้:
- การแยกระหว่างการทดสอบหน่วยและการทดสอบการรวม
- เนื่องจากบริการ CI ทั้งหมดทำงานบนคอนเทนเนอร์ การทดสอบที่ทำงานในเครื่องและใน CI จะมีพฤติกรรมเหมือนกัน ซึ่งช่วยให้มั่นใจได้ถึงความสอดคล้องของพฤติกรรมการทดสอบในเครื่องท้องถิ่นและเซิร์ฟเวอร์ CI
- การใช้คอนเทนเนอร์เพื่อทำให้แอปพลิเคชันใช้งานได้ช่วยให้มั่นใจถึงความสอดคล้องในเซิร์ฟเวอร์ที่ใช้งานจริง
- การทดสอบที่เชื่อถือได้ เนื่องจากมีการจัดการการขึ้นต่อกันที่ดี
- การทดสอบแบบพกพา ใช้ความพยายามเพียงเล็กน้อยในการตั้งค่าเมื่อใช้โดยนักพัฒนาหลายคน