Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ตั้งค่าคอนเทนเนอร์ Docker เพื่อทดสอบแอป Rails ของคุณ

ในฐานะนักพัฒนา 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

ตอนนี้ หากเรากลับไปตรวจสอบแอปในเบราว์เซอร์ เราจะได้รับการตอบสนองที่คาดหวัง:

ตั้งค่าคอนเทนเนอร์ Docker เพื่อทดสอบแอป Rails ของคุณ

โปรดทราบว่าเรากำลังเริ่มต้นเซิร์ฟเวอร์โดยใช้การจำลอง 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
  • การใช้คอนเทนเนอร์เพื่อทำให้แอปพลิเคชันใช้งานได้ช่วยให้มั่นใจถึงความสอดคล้องในเซิร์ฟเวอร์ที่ใช้งานจริง
  • การทดสอบที่เชื่อถือได้ เนื่องจากมีการจัดการการขึ้นต่อกันที่ดี
  • การทดสอบแบบพกพา ใช้ความพยายามเพียงเล็กน้อยในการตั้งค่าเมื่อใช้โดยนักพัฒนาหลายคน