Computer >> Máy Tính >  >> Lập trình >> Ruby

Thiết lập một vùng chứa Docker để kiểm tra ứng dụng Rails của bạn

Là nhà phát triển Ruby / Rails, chúng tôi thích viết các bài kiểm tra. Kiểm thử là một phần không thể thiếu trong phát triển phần mềm. Các bài kiểm tra tốt giúp chúng tôi viết mã chất lượng cao. Chúng tăng thêm giá trị cho quá trình phát triển tổng thể, nhưng nếu chúng ta không quản lý tốt việc kiểm tra, chúng có thể làm chúng ta chậm lại. Dưới đây là một số triệu chứng của các xét nghiệm được quản lý không đúng cách:

  • Việc chạy thử nghiệm mất nhiều thời gian.
  • Các bài kiểm tra không đáng tin cậy và thất bại một cách ngẫu nhiên.
  • Các bài kiểm tra hoạt động khác nhau trên các máy khác nhau.
  • Việc chạy tất cả các bộ thử nghiệm sẽ làm chậm CI của bạn.

Trong bài đăng trên blog này, chúng tôi sẽ trình bày cách sử dụng các vùng chứa để làm cho việc kiểm tra trở nên đáng tin cậy, điều này rất hữu ích trong việc cung cấp phần mềm chất lượng cao.

Kiểm tra Tích hợp và Kiểm tra Đơn vị

Trước khi chuyển sang chủ đề về vùng chứa, trước tiên chúng ta hãy hiểu và cùng tìm hiểu về sự khác biệt giữa các bài kiểm tra đơn vị và tích hợp.

Kiểm tra đơn vị là một cách để kiểm tra một khối mã riêng biệt. Chúng giúp kiểm tra chức năng của khối mã nhưng không giúp kiểm tra các phần phụ thuộc. Ví dụ, đây là một phương pháp đơn giản:

def sum(a, b)
  a + b
end

Đây là một hàm tính tổng hai số, rất đơn giản. Kiểm tra chức năng này sẽ rất dễ dàng. Nhưng điều gì sẽ xảy ra nếu hàm này có sự phụ thuộc vào một dịch vụ bên ngoài. Phương thức này phải ghi vào cơ sở dữ liệu khi nó hoàn tất tính tổng.

  def sum!(a, b)
    c = a + b
    Sum.create(value: c)
  end

Trong khối mã này, các giá trị được thêm vào và lưu vào cơ sở dữ liệu. Để kiểm tra khối mã này, chúng ta cần có một máy chủ cơ sở dữ liệu đang chạy. Nếu chúng tôi kiểm tra điều này với máy chủ cơ sở dữ liệu đang chạy, nó không còn là kiểm tra đơn vị nữa. Điều này cần sự phụ thuộc bên ngoài và chúng tôi cũng sẽ kiểm tra kết nối cơ sở dữ liệu và liệu bản ghi có được lưu trong cơ sở dữ liệu hay không. Trong trường hợp này, thử nghiệm này là thử nghiệm tích hợp.

Kiểm tra đơn vị có thể là kiểm tra đơn vị đơn lẻ (mô phỏng / sơ khai tất cả các phần phụ thuộc) hoặc bài kiểm tra đơn vị hòa đồng (cho phép nói chuyện với các phụ thuộc khác). Mọi người đã đề xuất các định nghĩa khác nhau về các bài kiểm tra đơn vị. Trong ngữ cảnh blog này, loại kiểm thử đơn vị mà chúng ta đang nói đến là kiểm thử đơn vị đơn lẻ.

Loại bỏ các phần phụ thuộc bên ngoài bằng mocks giúp các bài kiểm tra chạy nhanh hơn. Trong khi sử dụng các bài kiểm tra đơn vị, chúng tôi loại bỏ các phần phụ thuộc này, nhưng để đảm bảo ứng dụng hoạt động như mong đợi, việc kiểm tra các phần phụ thuộc cũng quan trọng không kém. Các bài kiểm tra mà chúng tôi viết để kiểm tra các phụ thuộc bên ngoài này, chẳng hạn như cơ sở dữ liệu, cuộc gọi mạng và tệp, là bài kiểm tra tích hợp .

Giống như các bài kiểm tra đơn vị, mọi người đã đề xuất các định nghĩa khác nhau về các bài kiểm tra tích hợp. Tuy nhiên, khái niệm cơ bản là như nhau. Sự khác biệt chỉ là trong phạm vi. Một chức năng nhỏ ghi vào cơ sở dữ liệu có thể là một bài kiểm tra tích hợp. Một chức năng rộng hơn kết nối nhiều phần của hệ thống cũng là một thử nghiệm tích hợp. Thử nghiệm mà chúng ta đang đề cập ở đây tập trung nhiều hơn vào phần hẹp. Bài kiểm tra càng hẹp, chúng ta càng cần viết nhiều bài kiểm tra. Tuy nhiên, với các thử nghiệm rộng hơn, số lượng thử nghiệm cần thiết giảm xuống.

Trong khi chạy kiểm tra tích hợp, chúng tôi cần ứng dụng và các phụ thuộc, chẳng hạn như cơ sở dữ liệu và các dịch vụ bên ngoài, để chạy. Quản lý những phụ thuộc này có thể là một thách thức.

Các nguyên tắc cơ bản của Docker

Docker là một công nghệ giúp trừu tượng hóa một ứng dụng khỏi môi trường của nó (hệ điều hành) và chạy nó một cách riêng biệt với máy chủ. Điều này tương tự như một máy ảo nhưng nhanh hơn và hiệu quả hơn. Docker cho phép chúng tôi đóng gói mã ứng dụng của mình và các phụ thuộc của nó vào một vùng chứa duy nhất. Điều này giúp làm cho mã di động và có nghĩa là chúng ta có thể có cùng một hình ảnh Docker chạy trong quá trình phát triển, thử nghiệm và sản xuất. Điều này sẽ đảm bảo tính nhất quán của môi trường mà ứng dụng chạy. Vì nó không phụ thuộc vào máy chủ lưu trữ, nó làm cho hành vi của ứng dụng có thể dự đoán được trong mọi môi trường. Docker giúp các nhà phát triển chạy và quản lý ứng dụng cũng như môi trường của nó, bao gồm xây dựng, chạy, triển khai và cập nhật bằng các lệnh đơn giản.

Hãy cho chúng tôi hiểu một số thuật ngữ quan trọng:

  • Dockerfile - Dockerfile chỉ đơn giản là một tệp mà chúng tôi xác định các phụ thuộc cần thiết cho ứng dụng. Chúng tôi có thể chỉ định hệ điều hành nó cần, thư viện máy khách cơ sở dữ liệu hoặc bất kỳ gói nào mà ứng dụng yêu cầu. Ở đây, chúng tôi cũng xác định lệnh cần thiết để ứng dụng chạy. Dockerfile bắt đầu từ một hình ảnh cơ sở. Những hình ảnh cơ sở này có thể là một hệ điều hành hoặc một ngôn ngữ lập trình.

  • Hình ảnh - Hình ảnh là một bản thiết kế cho biết các bước cần thiết để một vùng chứa chạy. Hình ảnh được xây dựng với các lớp khác nhau. Mỗi bước được định nghĩa trong Dockerfile là một lớp trong hình ảnh. Điều này làm tăng khả năng tái sử dụng và giúp lớp trung gian lưu vào bộ nhớ đệm để xây dựng hình ảnh nhanh hơn.

  • Vùng chứa - Hộp đựng là hình ảnh thực tế. Chúng cũng có thể ở trạng thái dừng. Các vùng chứa có thể được bắt đầu từ một hình ảnh docker. Một hình ảnh docker duy nhất có thể bắt đầu nhiều vùng chứa docker. Vùng chứa chứa thông tin được chỉ định bởi hình ảnh. Có một lớp vùng chứa mỏng nơi lưu trữ tất cả các thay đổi được thực hiện đối với hình ảnh trong khi ứng dụng đang chạy.

  • Docker Compose - Docker Compose là một công cụ khác giúp quản lý nhiều vùng chứa. Chúng ta có thể có nhiều vùng chứa từ cùng một hình ảnh docker. Chúng tôi cũng có thể cần nhiều vùng chứa từ các hình ảnh docker khác nhau và các vùng chứa này cần tương tác với nhau. Docker Compose quản lý các vùng chứa. Có sẵn các công cụ khác, nhưng trong ngữ cảnh này, chúng tôi sẽ chỉ sử dụng Docker Compose vì tính đơn giản của nó.

Máy ảo nằm trên máy chủ siêu giám sát và cần cài đặt hệ điều hành riêng biệt, do đó, nhiều tài nguyên máy chủ được sử dụng hơn. Tuy nhiên, với Docker, chúng ta chỉ cần cài đặt một công cụ Docker trong hệ điều hành máy chủ và việc quản lý tài nguyên máy chủ do Docker thực hiện. Docker giúp cuộc sống của cả nhà phát triển và sysops trở nên dễ dàng hơn, vì vậy nó rất phổ biến trong văn hóa DevOps.

Tại sao nên sử dụng Docker để thử nghiệm

Docker cô lập ứng dụng đang chạy hoặc thử nghiệm khỏi môi trường máy chủ của nó với mức tiêu thụ tài nguyên tối thiểu. Dưới đây là một số lợi ích của việc sử dụng docker để thử nghiệm:

  • Nhẹ: Hộp đựng Docker có trọng lượng nhẹ. Điều này giúp chạy nhiều vùng chứa cùng một lúc và không có một quá trình thử nghiệm nào gây ra sự cố trong một quá trình thử nghiệm khác.

  • Di động: Hình ảnh Docker có tính di động và có thể chạy trong mọi môi trường. Điều này giúp ích cho CI, vì chúng tôi có thể sử dụng lại cùng một hình ảnh để chạy thử nghiệm trong môi trường cục bộ và môi trường CI.

  • Phiên bản: Việc sử dụng nhiều ứng dụng hoặc nâng cấp phần phụ thuộc của chúng tôi yêu cầu các phiên bản phụ thuộc khác nhau trong máy cục bộ của chúng tôi. Điều này sẽ rất khó quản lý nếu không có Docker. Với Docker, chúng ta có thể có các vùng chứa khác nhau đang chạy với một phiên bản khác. Chúng tôi cũng có thể chạy thử nghiệm trên các phiên bản khác nhau và xác minh mọi thứ.

  • Quản lý dễ dàng các dịch vụ vi mô :Với microservices, việc kiểm tra cũng trở nên phân tán và phức tạp. Điều này có nghĩa là chúng ta cần quản lý dịch vụ nhỏ phụ thuộc. Docker và Docker Compose trợ giúp để quản lý các phụ thuộc dịch vụ này.

Điều kiện tiên quyết

Để thử điều này, hãy đảm bảo những điều sau được thiết lập:

  • Docker
  • Docker-soạn
  • Ruby 2.7 (Tùy chọn, vì chúng tôi sẽ thêm Ruby vào vùng chứa Docker, nhưng nó sẽ hữu ích cho việc thử nghiệm)
  • Đường ray (khuyến nghị 5.1+)

Tài liệu hóa ứng dụng

Chúng ta hãy bắt đầu bằng cách tạo một ứng dụng Rails đơn giản và chứa nó. Tôi đang bắt đầu với một ứng dụng Rails mới. Bạn cũng có thể chứa ứng dụng Rails hiện có.

rails new calculator

Tạo một tệp có tên Dockerfile với nội dung sau:

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 mà chúng tôi đã xác định có hình ảnh cơ sở Ruby 2.7. Chúng ta cần NodeJS và Yarn, vì vậy bước tiếp theo là cài đặt chúng. Sau khi chúng được cài đặt, chúng tôi chạy bundle installyarn install . Cuối cùng, chúng tôi sao chép tất cả mã và chạy máy chủ Rails với 3000 cổng được xuất. Đây là một Dockerfile rất đơn giản. Với điều này, ứng dụng Rails của chúng tôi được làm dày thêm.

Bây giờ chúng ta có thể tạo và chạy hình ảnh docker để đảm bảo rằng nó đang chạy.

Xây dựng hình ảnh docker:

docker build . -t  calculator

Chạy vùng chứa:

docker run -p 3000:3000 calculator

Tạo Mô hình với Logic Tính toán

Hãy để chúng tôi tạo một mô hình được gọi là tính toán, nơi chúng tôi có thể thêm logic tính toán của mình. Chúng tôi sẽ sử dụng mô hình này để viết một số logic và bài kiểm tra cho chúng. Chúng tôi có thể xem xét cách chúng tôi có thể quản lý sự phụ thuộc của các thử nghiệm này bằng Docker Compose.

Chúng tôi hiện đang chạy Rails với SQLite. Hãy để chúng tôi thay đổi nó thành Postgres thay vì SQLite. Điều này có thể được thực hiện dễ dàng bằng lệnh sau:

rails db:system:change --to=postgresql

Tạo mô hình tính toán:

bundle exec rails generate model calculation

Thêm một số trường trong lược đồ tính toán:

# 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

Tiếp theo, hãy thử chạy cơ sở dữ liệu khi di chuyển bên trong vùng chứa:

docker-compose exec web rails db:migrate

Vì cơ sở dữ liệu không chạy bên trong vùng chứa, nên việc chạy lệnh này sẽ gặp lỗi.

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"?

Chúng ta có thể chạy một hình ảnh Postgres dưới dạng một hình ảnh khác và thêm một mạng lưới giữa chúng. Để quản lý giao tiếp vùng chứa này, chúng ta có thể sử dụng Docker Compose.

Sử dụng Docker Compose để quản lý nhiều vùng chứa

Với ứng dụng Rails được chứa, chúng ta có thể thêm các phần phụ thuộc vào dịch vụ bằng Docker Compose. Phần phụ thuộc vào ứng dụng là Postgres.

Hình ảnh Postgres Docker có sẵn công khai, vì vậy chúng tôi không cần tạo một hình ảnh. Tuy nhiên, chúng ta cần quản lý vùng chứa cơ sở dữ liệu và vùng chứa ứng dụng Rails của mình.

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

Bây giờ, hãy truy cập database.yaml và thay đổi cơ sở dữ liệu sang một máy chủ lưu trữ khác. Máy chủ lưu trữ sẽ là tên được chỉ định bên trong dịch vụ trong tệp Docker Compose. Trong trường hợp của chúng tôi, máy chủ lưu trữ là db . Đảm bảo rằng mật khẩu DB giống nhau trong postgres dịch vụ và web Dịch vụ. Ngoài ra, hãy thay đổi tên người dùng, mật khẩu và tên cơ sở dữ liệu như được chỉ định trong tệp Docker Compose.

# database.yaml

development:
  <<: *default
  database: calculator_development
  username: calculator
  password: <%= ENV['CALCULATOR_DATABASE_PASSWORD'] %>

Bây giờ, chúng ta có thể bắt đầu các vùng chứa bằng cách sử dụng một lệnh duy nhất:

docker-compose up

Thao tác này sẽ khởi động cả cơ sở dữ liệu và máy chủ Rails của chúng tôi. Chúng tôi có thể chạy lệnh di chuyển ngay bây giờ và nó sẽ có thể kết nối với Postgres và chạy quá trình di chuyển.

Thêm thử nghiệm

Bây giờ chúng ta đã có tất cả các thiết lập sẵn sàng, hãy viết một phương thức đơn giản bên trong mô hình và kiểm tra phương thức:

# calculation.rb

class Calculation < ApplicationRecord

  def self.sum(x, y)
    result = x + y
    Calculation.create(x: x, y: y, result: result)
    result
  end

end

Phương thức tổng này không chỉ thêm số mà còn lưu số vào cơ sở dữ liệu. Vì vậy, việc thử nghiệm phương pháp này yêu cầu một phiên bản cơ sở dữ liệu đang chạy.

Thử nghiệm này sẽ là một thử nghiệm tích hợp, vì chúng tôi phải kết nối với cơ sở dữ liệu.

Chúng tôi sẽ sử dụng Mini-test để viết thử nghiệm, đây là mặc định của 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

Chạy thử nghiệm:

docker-compose exec web rails test

Trong thử nghiệm trên, chúng tôi đang kiểm tra xem phương thức sum có đang thêm các giá trị và lưu các giá trị trong cơ sở dữ liệu hay không. Với Docker Compose, chúng tôi có một cách rất dễ dàng để kết nối với cơ sở dữ liệu.

Ở đây, phương pháp phụ thuộc vào cơ sở dữ liệu. Phần phụ thuộc có thể không chỉ là cơ sở dữ liệu mà còn là dịch vụ của bên thứ ba cung cấp API REST. Vì vậy, chúng ta hãy thử sử dụng dịch vụ của bên thứ ba cung cấp sum mà chúng tôi có thể sử dụng thay vì viết của riêng mình.

# 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

Chúng tôi có thể làm điều gì đó tương tự đối với API REST mà chúng tôi đã làm cho cơ sở dữ liệu như một phần phụ thuộc. Chúng tôi cần đảm bảo rằng đây là các điểm cuối hợp lệ, nhưng chúng tôi không muốn thực hiện cuộc gọi đến dịch vụ thực tế trong quá trình thử nghiệm. Đối với trường hợp này, chúng tôi có thể tạo các bài kiểm tra giả / sơ khai, nhưng những bài kiểm tra này sẽ không phải là bài kiểm tra tích hợp nếu chúng tôi làm như vậy. Chúng tôi muốn đảm bảo rằng thử nghiệm càng thực tế càng tốt. Vì mục đích này, chúng tôi có thể tạo một điểm cuối API giả, điểm cuối này chúng tôi sẽ chạy trong một vùng chứa và thực hiện cuộc gọi đến dịch vụ API vùng chứa giả, dịch vụ này sẽ phản hồi lại kết quả cho chúng tôi.

Trước tiên, hãy để chúng tôi sửa đổi bài kiểm tra:

# 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

Ở đây, chúng tôi đã sửa đổi trường hợp thử nghiệm để chỉ kiểm tra xem kết quả do API tổng cung cấp có được lưu đúng cách trong cơ sở dữ liệu hay không. Chúng tôi không cần kiểm tra xem giá trị thực của tổng có chính xác hay không, vì điều này được xử lý bởi dịch vụ bên ngoài và chúng tôi tin tưởng họ.

Để tạo một máy chủ giả, chúng tôi sẽ sử dụng openAPI. Hãy để chúng tôi tạo một định nghĩa đơn giản:

Tạo một thư mục mới trong thư mục gốc và đặt tên cho nó là mock . Bên trong thư mục, tạo tên tệp 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

Để chạy máy chủ này dựa trên đặc điểm kỹ thuật, chúng tôi có thể sử dụng bất kỳ nhà cung cấp dịch vụ giả nào. Tôi sẽ sử dụng một công cụ gọi là lăng kính. Nó có sẵn một hình ảnh docker, chúng tôi có thể thêm hình ảnh này vào tệp docker-soạn của mình:

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

Bây giờ, hãy chạy thử nghiệm và xem nó có hoạt động như mong đợi hay không:

> 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

Ừ! Bây giờ chúng tôi đã chạy thử nghiệm. Phương thức kết nối với hình ảnh Docker dịch vụ tổng để lấy tổng và kết nối với Postgres để lưu dữ liệu. Tất cả các phụ thuộc được quản lý bởi Docker Compose, vì vậy chúng ta không cần phải lo lắng về các phụ thuộc đã được cài đặt trước đó. Việc chuyển bài kiểm tra này sang bất kỳ máy nào sẽ hoạt động mà không cần bất kỳ cài đặt phụ thuộc nào. Thứ duy nhất mà máy cần là một đế cắm.

Nếu ứng dụng của bạn phụ thuộc vào bất kỳ phụ thuộc nào khác, chẳng hạn như Redis hoặc Memcached, bạn có thể tìm thấy chúng trong dockerhub. Chúng tôi có thể thêm những hình ảnh này vào tệp Docker Compose nếu cần.

Thử nghiệm song song

Rails 5.1.5+ hỗ trợ chạy thử nghiệm song song. Theo mặc định, các bài kiểm tra chạy song song từ Rails 6 dựa trên số lượng bộ xử lý. Trong khi chạy thử nghiệm, nó tạo ra các cá thể cơ sở dữ liệu dựa trên số lượng thử nghiệm song song. Nếu chúng ta có bốn bộ xử lý, nó sẽ tạo ra các cơ sở dữ liệu "Calculator_test-0", Calculator_test-1, Calculator_test-2 và Calculator_test-3. Nếu bạn vào postgres cli và kiểm tra cơ sở dữ liệu, bạn sẽ thấy như sau:

> \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 |

Bây giờ, ngay cả khi bạn không sử dụng Rails hoặc các bài kiểm tra nhỏ hoặc có phiên bản Rails cũ hơn, bạn vẫn chạy các bài kiểm tra song song nếu bạn viết chúng bằng Docker. Rails tự động tạo cơ sở dữ liệu cho các thử nghiệm song song, nhưng nếu có bất kỳ phần phụ thuộc nào khác, chẳng hạn như Redis hoặc memcached, chúng có thể phải được quản lý theo cách thủ công.

Vì chúng tôi đã quản lý tất cả các phần phụ thuộc của mình, chúng tôi có thể chạy Docker Compose với các đối số khác nhau để chạy các bài kiểm tra song song. Vì mục đích này, chúng tôi có thể chỉ định thư mục nào chúng tôi muốn chạy thử nghiệm song song, thực thi tạo nhiều tệp Docker Compose và chạy chúng song song.

Thêm Makefile để Đơn giản hóa Lệnh

Vì chúng tôi đã sử dụng các lệnh Docker và Docker Compose để chạy các bài kiểm tra của mình, các lệnh sẽ dài. Chúng có thể khó nhớ và khó chạy trong khi phát triển từng ngày. Để đơn giản hóa nó thành một lệnh đơn giản, chúng ta hãy sử dụng add Makefile. Makefile là một tệp văn bản đơn giản được sử dụng để tham chiếu việc thực thi lệnh đích.

.PHONY: test
up:
  docker-compose up
down:
  docker-compose down
test:
  docker-compose exec web rails test

Chúng tôi đã thêm một vài lệnh vào makefile. Điều này có thể được mở rộng theo yêu cầu của ứng dụng. Thêm makefile vào thư mục gốc của ứng dụng Rails. Khi chúng ta có makefile này, chúng ta có thể chỉ cần chạy thử nghiệm bằng lệnh sau:

Khởi động các vùng chứa:

> make up

Chạy thử nghiệm:

> make test

Sử dụng Docker for Local Development

Chúng tôi cũng có thể sử dụng Docker trong khi phát triển ứng dụng. Điều này giúp đảm bảo rằng ứng dụng hoạt động nhất quán trong quá trình thử nghiệm, phát triển và sản xuất. Hãy để chúng tôi xem lại những gì chúng tôi cần thay đổi để sử dụng vùng chứa trên cho sự phát triển cục bộ.

Ổ cắm

Trong khi chạy ứng dụng cục bộ, chúng tôi muốn ứng dụng phản ánh các thay đổi trong máy chủ cục bộ khi chúng tôi thực hiện các thay đổi trong mã. Trong trường hợp hiện tại, mã được sao chép từ máy của chúng tôi sang Docker và máy chủ được khởi động. Vì vậy, nếu chúng tôi thực hiện các thay đổi đối với tệp trong máy cục bộ, chúng sẽ không được phản ánh trong Docker. Để đảm bảo rằng mã luôn được đồng bộ, chúng tôi có thể sử dụng volume mount.

Trong tệp Docker Compose, hãy để chúng tôi thêm các tập:

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

Bây giờ, nếu chúng tôi thực hiện bất kỳ thay đổi nào đối với máy chủ, các thay đổi sẽ được phản ánh. Hãy để chúng tôi thử điều này bằng cách tạo một bộ điều khiển và gọi phương thức tổng của chúng tôi:

# controllers/calculation_controller.rb
class CalculationController < ApplicationController
  def index
    result = Calculation.sum(params[:x], params[:y])
    render json: {result: result}
  end
end

Thêm tuyến gốc:

# 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

Bây giờ, nếu chúng tôi quay lại và kiểm tra ứng dụng trong trình duyệt, chúng tôi sẽ nhận được phản hồi mong đợi:

Thiết lập một vùng chứa Docker để kiểm tra ứng dụng Rails của bạn

Lưu ý rằng chúng tôi đang khởi động máy chủ bằng sumservice giả , vì vậy, bất kể những gì chúng tôi cung cấp trong URL dưới dạng tham số truy vấn, nó sẽ luôn trả về 2. Đây có vẻ không phải là một cách tốt để phát triển nhưng sử dụng một nhà cung cấp dịch vụ giả có những lợi thế của nó. Khi phát triển microservices, có thể có rất nhiều dịch vụ phụ thuộc, chúng ta sẽ cần khởi động trước khi bắt đầu dịch vụ. Điều này giúp tăng năng suất trong quá trình phát triển. Chúng tôi không quan tâm đến dịch vụ khác và chỉ quan tâm đến dịch vụ mà chúng tôi đang phát triển.

Nếu chúng tôi muốn xác minh lần cuối về cách nó hoạt động với dịch vụ thực tế, chúng tôi có thể làm như vậy bằng cách tạo tệp Docker Compose mới và thay thế nó bằng hình ảnh thực tế của dịch vụ. Nếu đó là một dịch vụ nội bộ, chúng tôi có thể chỉ cần thay thế nó bằng hình ảnh Docker ổn định mới nhất. Tôi khuyên bạn nên có một dịch vụ giả ngay cả trong môi trường phát triển.

. dockerignore

Khi ứng dụng phát triển lớn hơn, các phụ thuộc sẽ khiến bối cảnh xây dựng Docker lớn hơn. Với bối cảnh xây dựng lớn hơn, cần nhiều thời gian hơn để xây dựng hình ảnh docker. Ngay cả việc gắn kết âm lượng cũng có thể gây ra độ trễ khi kích thước ứng dụng tăng lên. Trong trường hợp này, chúng tôi có thể thêm tệp không mong muốn vào .dockerignore để giảm bối cảnh xây dựng.

.git*
log/*
README.md
node_modules

Entrypoint

Trong khi máy chủ Rails đang chạy trong vùng chứa, đôi khi chúng tôi nhận được thông báo lỗi cho biết máy chủ đã chạy vì tệp PID không bị xóa khi vùng chứa thoát. Để loại bỏ lỗi này, chúng tôi có thể thêm ENTRYPOINT, thao tác này sẽ xóa server.pid tệp.

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"]

Tạo entrypoint.sh tệp:

#!/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 "$@"

Làm cho nó có thể thực thi được

chmod +x entrypoint.sh

Bây giờ, khi chúng tôi chạy hình ảnh Docker, chúng tôi sẽ không gặp sự cố này.

Kết luận

Điều rất quan trọng là phải phân biệt các bài kiểm tra tích hợp và bài kiểm tra đơn vị. Trong khi viết các bài kiểm tra đơn vị, chúng ta có thể mô phỏng các phần phụ thuộc bên ngoài, chẳng hạn như cơ sở dữ liệu. Điều này sẽ giúp kiểm tra mã một cách riêng biệt. Điều quan trọng không kém là chạy các bài kiểm tra tích hợp giữa logic nghiệp vụ cốt lõi và các phụ thuộc của nó. Các phần phụ thuộc, chẳng hạn như cơ sở dữ liệu và các dịch vụ bên ngoài, có thể được kiểm tra trong tích hợp bằng cách sử dụng Docker và Docker Compose. Đó là thực hành tốt để tách hai điều này. Chúng tôi sẽ có thể chạy chúng một cách riêng biệt. Có nhiều bài kiểm tra đơn vị hơn và ít bài kiểm tra tích hợp hơn làm cho các bài kiểm tra của bạn chạy nhanh hơn và đáng tin cậy hơn.

Một số ưu điểm của quy trình làm việc này như sau:

  • Phân biệt giữa các bài kiểm tra đơn vị và tích hợp.
  • Vì tất cả các dịch vụ CI đều chạy trên vùng chứa, các bài kiểm tra đang chạy cục bộ và trong CI sẽ có cùng hoạt động. Điều này giúp đảm bảo tính nhất quán của hoạt động kiểm tra trong máy cục bộ và máy chủ CI.
  • Việc sử dụng vùng chứa để triển khai ứng dụng cũng giúp đảm bảo tính nhất quán trong máy chủ sản xuất.
  • Các bài kiểm tra đáng tin cậy, vì các phần phụ thuộc được quản lý tốt.
  • Thử nghiệm di động, cần ít nỗ lực trong quá trình thiết lập khi được nhiều nhà phát triển sử dụng.