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

Thử nghiệm Bash với BATS

Các nhà phát triển phần mềm viết ứng dụng bằng các ngôn ngữ như Java, Ruby và Python có các thư viện phức tạp để giúp họ duy trì tính toàn vẹn của phần mềm theo thời gian. Họ tạo các bài kiểm tra chạy các ứng dụng thông qua một loạt các lần thực thi trong môi trường có cấu trúc để đảm bảo tất cả các khía cạnh phần mềm của họ hoạt động như mong đợi.

Các bài kiểm tra này thậm chí còn mạnh mẽ hơn khi chúng được tự động hóa trong hệ thống tích hợp liên tục (CI), nơi mỗi lần đẩy đến kho lưu trữ nguồn đều khiến các bài kiểm tra chạy và các nhà phát triển được thông báo ngay lập tức khi các bài kiểm tra không thành công. Phản hồi nhanh này làm tăng niềm tin của nhà phát triển vào tính toàn vẹn chức năng của các ứng dụng của họ.

Hệ thống kiểm tra tự động Bash (BATS) cho phép các nhà phát triển viết các tập lệnh và thư viện Bash áp dụng các phương pháp tương tự được sử dụng bởi Java, Ruby, Python và các nhà phát triển khác cho mã Bash của họ.

Cài đặt BATS

Trang BATS GitHub bao gồm hướng dẫn cài đặt. Có hai thư viện trình trợ giúp BATS cung cấp các xác nhận mạnh mẽ hơn hoặc cho phép ghi đè lên định dạng đầu ra Test Anything Protocol (TAP) được BATS sử dụng. Chúng có thể được cài đặt ở một vị trí tiêu chuẩn và có nguồn gốc bởi tất cả các tập lệnh. Có thể thuận tiện hơn khi bao gồm phiên bản hoàn chỉnh của BATS và các thư viện trợ giúp của nó trong kho lưu trữ Git cho mỗi bộ tập lệnh hoặc thư viện đang được kiểm tra. Điều này có thể được thực hiện bằng cách sử dụng git submodule hệ thống.

Các lệnh sau sẽ cài đặt BATS và các thư viện trợ giúp của nó vào kiểm tra thư mục trong kho lưu trữ Git.

git submodule init
git submodule add https://github.com/sstephenson/bats test/libs/bats
git submodule add https://github.com/ztombol/bats-assert test/libs/bats-assert
git submodule add https://github.com/ztombol/bats-support test/libs/bats-support
git add .
git commit -m 'installed bats'

Để sao chép kho lưu trữ Git và cài đặt các mô-đun con của nó cùng lúc, hãy sử dụng

--recurse-submodules gắn cờ cho git clone .

Mỗi tập lệnh kiểm tra BATS phải được thực thi bởi con dơi có thể thực thi. Nếu bạn đã cài đặt BATS vào test / libs repo mã nguồn của mình thư mục, bạn có thể gọi kiểm tra bằng:

./test/libs/bats/bin/bats <path to test script>

Ngoài ra, hãy thêm phần sau vào đầu mỗi tập lệnh thử nghiệm BATS của bạn:

#!/usr/bin/env ./test/libs/bats/bin/bats
load 'libs/bats-support/load'
load 'libs/bats-assert/load'

chmod + x <đường dẫn đến tập lệnh thử nghiệm> . Điều này sẽ a) làm cho chúng có thể thực thi được với BATS được cài đặt trong ./test/libs/bats và b) bao gồm các thư viện trợ giúp này. Các tập lệnh kiểm tra BATS thường được lưu trữ trong kiểm tra và được đặt tên cho tập lệnh đang được kiểm tra, nhưng với .bats sự mở rộng. Ví dụ:tập lệnh BATS kiểm tra bin / build nên được gọi là test / build.bats .

Bạn cũng có thể chạy toàn bộ tập hợp các tệp kiểm tra BATS bằng cách chuyển một biểu thức chính quy tới BATS, ví dụ: ./test/lib/bats/bin/bats test / *. Bats .

Tổ chức thư viện và tập lệnh cho phạm vi BATS

Các tập lệnh và thư viện Bash phải được tổ chức theo cách thể hiện hiệu quả hoạt động bên trong của chúng với BATS. Nói chung, các hàm thư viện và tập lệnh shell chạy nhiều lệnh khi chúng được gọi hoặc thực thi không thể đáp ứng được việc kiểm tra BATS hiệu quả.

Ví dụ:build.sh là một script điển hình mà nhiều người viết. Về cơ bản nó là một đống mã lớn. Một số thậm chí có thể đặt đống mã này vào một hàm trong thư viện. Nhưng không thể chạy một đống mã lớn trong thử nghiệm BATS và bao gồm tất cả các loại lỗi có thể gặp phải trong các trường hợp thử nghiệm riêng biệt. Cách duy nhất để kiểm tra đống mã này với mức độ phù hợp là chia nó thành nhiều chức năng nhỏ, có thể tái sử dụng và quan trọng nhất là có thể kiểm tra độc lập.

Thật đơn giản khi thêm nhiều chức năng vào thư viện. Một lợi ích bổ sung là một số chức năng này có thể trở nên hữu ích một cách đáng ngạc nhiên theo đúng nghĩa của chúng. Khi bạn đã chia nhỏ chức năng thư viện của mình thành nhiều chức năng nhỏ hơn, bạn có thể mã nguồn thư viện trong bài kiểm tra BATS của bạn và chạy các chức năng như bạn làm với bất kỳ lệnh nào khác để kiểm tra chúng.

Các tập lệnh Bash cũng phải được chia nhỏ thành nhiều chức năng, mà phần chính của tập lệnh sẽ gọi khi tập lệnh được thực thi. Ngoài ra, có một thủ thuật rất hữu ích giúp bạn kiểm tra tập lệnh Bash với BATS dễ dàng hơn nhiều:Lấy tất cả mã được thực thi trong phần chính của tập lệnh và chuyển nó vào một hàm, được gọi là run_main . Sau đó, thêm phần sau vào cuối tập lệnh:

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]
then
  run_main
fi

Đoạn mã bổ sung này thực hiện một điều gì đó đặc biệt. Nó làm cho tập lệnh hoạt động khác khi nó được thực thi dưới dạng tập lệnh so với khi nó được đưa vào môi trường với source . Thủ thuật này cho phép kiểm tra tập lệnh giống như cách kiểm tra thư viện, bằng cách tìm nguồn cung cấp và kiểm tra các chức năng riêng lẻ. Ví dụ:đây là build.sh được cấu trúc lại để có khả năng kiểm tra BATS tốt hơn.

Viết và chạy kiểm tra

Như đã đề cập ở trên, BATS là một khung thử nghiệm tuân thủ TAP với cú pháp và đầu ra sẽ quen thuộc với những người đã sử dụng các bộ thử nghiệm tuân thủ TAP khác, chẳng hạn như JUnit, RSpec hoặc Jest. Các bài kiểm tra của nó được tổ chức thành các đoạn mã kiểm tra riêng lẻ. Các tập lệnh kiểm tra được tổ chức thành một hoặc nhiều @test mô tả các khối mô tả đơn vị của ứng dụng đang được kiểm tra. Mỗi @test khối sẽ chạy một loạt các lệnh chuẩn bị môi trường kiểm tra, chạy lệnh được kiểm tra và đưa ra các xác nhận về lối ra và đầu ra của lệnh được kiểm tra. Nhiều hàm xác nhận được nhập với con dơi , dơi-khẳng định hỗ trợ dơi thư viện, được tải vào môi trường ở đầu tập lệnh thử nghiệm BATS. Đây là một khối kiểm tra BATS điển hình:

@test "requires CI_COMMIT_REF_SLUG environment variable" {
  unset CI_COMMIT_REF_SLUG
  assert_empty "${CI_COMMIT_REF_SLUG}"
  run some_command
  assert_failure
  assert_output --partial "CI_COMMIT_REF_SLUG"
}

Nếu tập lệnh BATS bao gồm thiết lập và / hoặc giọt nước mắt các chức năng này được BATS tự động thực thi trước và sau khi chạy mỗi khối thử nghiệm. Điều này giúp bạn có thể tạo các biến môi trường, tệp thử nghiệm và thực hiện những việc khác cần thiết cho một hoặc tất cả các thử nghiệm, sau đó chia nhỏ chúng ra sau mỗi lần chạy thử nghiệm. Build.bats là bài kiểm tra BATS đầy đủ của build.sh mới được định dạng của chúng tôi script. ( mock_docker lệnh trong bài kiểm tra này sẽ được giải thích bên dưới, trong phần về chế nhạo / khai thác.)

Khi tập lệnh thử nghiệm chạy, BATS sử dụng thi hành để chạy từng @test khối như một quy trình con riêng biệt. Điều này giúp bạn có thể xuất các biến môi trường và thậm chí các hàm trong một @test mà không ảnh hưởng đến @test khác s hoặc làm ô nhiễm phiên trình bao hiện tại của bạn. Đầu ra của quá trình chạy thử nghiệm là một định dạng tiêu chuẩn mà con người có thể hiểu được và người tiêu dùng TAP phân tích cú pháp hoặc thao tác theo chương trình. Đây là ví dụ về kết quả đầu ra cho CI_COMMIT_REF_SLUG khối kiểm tra khi nó không thành công:

 ✗ requires CI_COMMIT_REF_SLUG environment variable
   (from function `assert_output' in file test/libs/bats-assert/src/assert.bash, line 231,
    in test file test/ci_deploy.bats, line 26)
     `assert_output --partial "CI_COMMIT_REF_SLUG"' failed

   -- output does not contain substring --
   substring (1 lines):
     CI_COMMIT_REF_SLUG
   output (3 lines):
     ./bin/deploy.sh: join_string_by: command not found
     oc error
     Could not login
   --

   ** Did not delete , as test failed **

1 test, 1 failure

Đây là kết quả của một bài kiểm tra thành công:

✓ requires CI_COMMIT_REF_SLUG environment variable

Người trợ giúp

Giống như bất kỳ tập lệnh hoặc thư viện shell nào, các tập lệnh thử nghiệm BATS có thể bao gồm các thư viện trợ giúp để chia sẻ mã chung trong các thử nghiệm hoặc nâng cao khả năng của chúng. Các thư viện trợ giúp này, chẳng hạn như bats-khẳng định hỗ trợ dơi , thậm chí có thể được kiểm tra bằng BATS.

Các thư viện có thể được đặt trong cùng một thư mục thử nghiệm với các tập lệnh BATS hoặc trong test / libs nếu số lượng tệp trong thư mục thử nghiệm khó sử dụng. BATS cung cấp tải hàm đưa đường dẫn đến tệp Bash liên quan đến tập lệnh đang được kiểm tra (ví dụ: kiểm tra , trong trường hợp của chúng tôi) và nguồn tệp đó. Các tệp phải kết thúc bằng tiền tố .bash , nhưng đường dẫn đến tệp được chuyển đến tải hàm không thể bao gồm tiền tố. build.bats tải bats-khẳng định hỗ trợ dơi thư viện, một helpers.bash nhỏ thư viện và docker_mock.bash thư viện (được mô tả bên dưới) với mã sau được đặt ở đầu tập lệnh thử nghiệm bên dưới dòng ma thuật thông dịch:

load 'libs/bats-support/load'
load 'libs/bats-assert/load'
load 'helpers'
load 'docker_mock'

Bắt đầu kiểm tra đầu vào và chế nhạo các cuộc gọi bên ngoài

Phần lớn các tập lệnh và thư viện Bash thực thi các chức năng và / hoặc thực thi khi chúng chạy. Thông thường, chúng được lập trình để hoạt động theo những cách cụ thể dựa trên trạng thái thoát hoặc đầu ra ( stdout , stderr ) của các chức năng hoặc tệp thực thi này. Để kiểm tra đúng các tập lệnh này, thường cần tạo các phiên bản giả của các lệnh này được thiết kế để hoạt động theo một cách cụ thể trong quá trình kiểm tra cụ thể, một quá trình được gọi là "sơ khai". Cũng có thể cần theo dõi chương trình đang được kiểm tra để đảm bảo nó gọi một lệnh cụ thể hoặc nó gọi một lệnh cụ thể với các đối số cụ thể, một quá trình được gọi là "mocking". Để biết thêm về điều này, hãy xem cuộc thảo luận tuyệt vời này về chế nhạo và khai thác trong Ruby RSpec, áp dụng cho bất kỳ hệ thống thử nghiệm nào.

Bash shell cung cấp các thủ thuật có thể được sử dụng trong các tập lệnh thử nghiệm BATS của bạn để thực hiện việc chế nhạo và khai báo. Tất cả đều yêu cầu sử dụng Bash xuất lệnh với -f cờ để xuất một chức năng ghi đè chức năng gốc hoặc thực thi. Điều này phải được thực hiện trước khi chương trình được thử nghiệm được thực thi. Dưới đây là một ví dụ đơn giản ghi đè cat thực thi:

function cat() { echo "THIS WOULD CAT ${*}" }
export -f cat

Phương thức này ghi đè một hàm theo cách tương tự. Nếu một thử nghiệm cần ghi đè một chức năng trong tập lệnh hoặc thư viện đang được thử nghiệm, thì điều quan trọng là phải tìm nguồn tập lệnh hoặc thư viện được thử nghiệm trước khi chức năng bị khai thác hoặc bị chế nhạo. Nếu không, đoạn văn bản gốc / mô hình sẽ được thay thế bằng hàm thực khi tập lệnh có nguồn gốc. Ngoài ra, hãy đảm bảo viết sơ khai / mô phỏng trước khi bạn chạy lệnh mà bạn đang thử nghiệm. Đây là một ví dụ từ build.bats điều đó chế nhạo việc tăng chức năng được mô tả trong build.sh để đảm bảo thông báo lỗi cụ thể được đưa ra bởi fuction đăng nhập:

@test ".login raises on oc error" {
  source ${profile_script}
  function raise() { echo "${1} raised"; }
  export -f raise
  run login
  assert_failure
  assert_output -p "Could not login raised"
}

Thông thường, không cần thiết phải hủy đặt một hàm sơ khai / giả lập sau khi kiểm tra, vì export chỉ ảnh hưởng đến quy trình con hiện tại trong quá trình thực thi của @test hiện tại khối. Tuy nhiên, có thể mô phỏng các lệnh / sơ khai (ví dụ: cat , sed , v.v.) mà BATS khẳng định * các chức năng sử dụng trong nội bộ. Các hàm giả / sơ khai này phải chưa được đặt trước khi các lệnh xác nhận này được chạy, hoặc chúng sẽ không hoạt động bình thường. Đây là một ví dụ từ build.bats điều đó chế nhạo sed , chạy build_deployable chức năng và hủy đặt sed trước khi chạy bất kỳ xác nhận nào:

@test ".build_deployable prints information, runs docker build on a modified Dockerfile.production and publish_image when its not a dry_run" {
  local expected_dockerfile='Dockerfile.production'
  local application='application'
  local environment='environment'
  local expected_original_base_image="${application}"
  local expected_candidate_image="${application}-candidate:${environment}"
  local expected_deployable_image="${application}:${environment}"
  source ${profile_script}
  mock_docker build --build-arg OAUTH_CLIENT_ID --build-arg OAUTH_REDIRECT --build-arg DDS_API_BASE_URL -t "${expected_deployable_image}" -
  function publish_image() { echo "publish_image ${*}"; }
  export -f publish_image
  function sed() {
    echo "sed ${*}" >&2;
    echo "FROM application-candidate:environment";
  }
  export -f sed
  run build_deployable "${application}" "${environment}"
  assert_success
  unset sed
  assert_output --regexp "sed.*${expected_dockerfile}"
  assert_output -p "Building ${expected_original_base_image} deployable ${expected_deployable_image} FROM ${expected_candidate_image}"
  assert_output -p "FROM ${expected_candidate_image} piped"
  assert_output -p "build --build-arg OAUTH_CLIENT_ID --build-arg OAUTH_REDIRECT --build-arg DDS_API_BASE_URL -t ${expected_deployable_image} -"
  assert_output -p "publish_image ${expected_deployable_image}"
}

Đôi khi cùng một lệnh, ví dụ:foo, sẽ được gọi nhiều lần, với các đối số khác nhau, trong cùng một hàm đang được kiểm tra. Những tình huống này yêu cầu tạo một tập hợp các chức năng:

  • mock_foo:nhận các đối số mong đợi làm đầu vào và lưu các đối số này vào tệp TMP
  • foo:phiên bản chế nhạo của lệnh, xử lý từng lệnh gọi với danh sách các đối số được mong đợi liên tục. Cái này phải được xuất bằng export -f.
  • cleanup_foo:xóa tệp TMP, để sử dụng trong các hàm xé nhỏ. Điều này có thể kiểm tra để đảm bảo rằng một khối @test đã thành công trước khi xóa.

Vì chức năng này thường được sử dụng lại trong các thử nghiệm khác nhau, nên tạo một thư viện trợ giúp có thể được tải giống như các thư viện khác.

Một ví dụ điển hình là docker_mock.bash . Nó được tải vào build.bats và được sử dụng trong bất kỳ khối thử nghiệm nào kiểm tra một hàm gọi tệp thi hành Docker. Một khối kiểm tra điển hình sử dụng docker_mock trông giống như:

@test ".publish_image fails if docker push fails" {
  setup_publish
  local expected_image="image"
  local expected_publishable_image="${CI_REGISTRY_IMAGE}/${expected_image}"
  source ${profile_script}
  mock_docker tag "${expected_image}" "${expected_publishable_image}"
  mock_docker push "${expected_publishable_image}" and_fail
  run publish_image "${expected_image}"
  assert_failure
  assert_output -p "tagging ${expected_image} as ${expected_publishable_image}"
  assert_output -p "tag ${expected_image} ${expected_publishable_image}"
  assert_output -p "pushing image to gitlab registry"
  assert_output -p "push ${expected_publishable_image}"
}

Thử nghiệm này thiết lập một kỳ vọng rằng Docker sẽ được gọi hai lần với các đối số khác nhau. Với cuộc gọi thứ hai đến Docker không thành công, nó sẽ chạy lệnh đã thử nghiệm, sau đó kiểm tra trạng thái thoát và các lệnh gọi dự kiến ​​tới Docker.

Một khía cạnh của BATS được giới thiệu bởi mock_docker.bash $ {BATS_TMPDIR} biến môi trường, mà BATS đặt ở đầu để cho phép các thử nghiệm và người trợ giúp tạo và hủy các tệp TMP ở một vị trí tiêu chuẩn. mock_docker.bash thư viện sẽ không xóa tập tin mô phỏng vẫn tồn tại của nó nếu thử nghiệm không thành công, nhưng nó sẽ in nơi nó được đặt để có thể xem và xóa nó. Bạn có thể cần phải dọn dẹp định kỳ các tệp giả cũ ra khỏi thư mục này.

Một lưu ý thận trọng liên quan đến chế độ chế nhạo / khai báo: build.bats kiểm tra vi phạm một cách có ý thức một quy tắc kiểm tra quy định rằng:Đừng chế nhạo những gì bạn không sở hữu! Câu lệnh này yêu cầu các lệnh gọi mà nhà phát triển thử nghiệm không viết, như docker , mèo , sed , v.v., nên được bao bọc trong các thư viện của riêng chúng, các thư viện này sẽ được chế tạo trong các thử nghiệm của các tập lệnh sử dụng chúng. Sau đó, các thư viện của trình bao bọc sẽ được kiểm tra mà không cần bắt chước các lệnh bên ngoài.

Đây là lời khuyên tốt và bỏ qua nó đi kèm với một cái giá phải trả. Nếu API Docker CLI thay đổi, các tập lệnh thử nghiệm sẽ không phát hiện ra thay đổi này, dẫn đến dương tính giả sẽ không hiển thị cho đến khi build.sh được thử nghiệm script chạy trong cài đặt sản xuất với phiên bản Docker mới. Các nhà phát triển thử nghiệm phải quyết định xem họ muốn tuân thủ tiêu chuẩn này nghiêm ngặt như thế nào, nhưng họ nên hiểu những đánh đổi liên quan đến quyết định của họ.

Kết luận

Việc giới thiệu một chế độ kiểm thử cho bất kỳ dự án phát triển phần mềm nào sẽ tạo ra sự cân bằng giữa a) sự gia tăng thời gian và tổ chức cần thiết để phát triển và duy trì mã và các bài kiểm tra và b) các nhà phát triển tin tưởng hơn vào tính toàn vẹn của ứng dụng trong suốt thời gian tồn tại của nó. Chế độ kiểm tra có thể không phù hợp với tất cả các tập lệnh và thư viện.

Nói chung, các tập lệnh và thư viện đáp ứng một hoặc nhiều điều sau đây nên được kiểm tra với BATS:

  • Chúng đáng được lưu trữ trong quyền kiểm soát nguồn
  • Chúng được sử dụng trong các quy trình quan trọng và dựa vào đó để chạy nhất quán trong một khoảng thời gian dài
  • Chúng cần được sửa đổi định kỳ để thêm / xóa / sửa đổi chức năng của chúng
  • Chúng được những người khác sử dụng

Sau khi quyết định áp dụng kỷ luật kiểm thử cho một hoặc nhiều tập lệnh hoặc thư viện Bash, BATS cung cấp các tính năng kiểm tra toàn diện có sẵn trong các môi trường phát triển phần mềm khác.

Lời cảm ơn:Tôi biết ơn Darrin Mann vì đã giới thiệu tôi tham gia thử nghiệm BATS.