Tách các bài kiểm tra đơn vị và bài kiểm tra tích hợp trong Go


97

Có phương pháp hay nhất được thiết lập để tách các bài kiểm tra đơn vị và bài kiểm tra tích hợp trong GoLang (làm chứng) không? Tôi có một kết hợp các bài kiểm tra đơn vị (không dựa vào bất kỳ tài nguyên bên ngoài nào và do đó chạy rất nhanh) và kiểm tra tích hợp (dựa vào bất kỳ tài nguyên bên ngoài nào và do đó chạy chậm hơn). Vì vậy, tôi muốn có thể kiểm soát việc có đưa các bài kiểm tra tích hợp vào hay không khi tôi nói go test.

Kỹ thuật đơn giản nhất dường như là để xác định một cờ-tích hợp trong chính:

var runIntegrationTests = flag.Bool("integration", false
    , "Run the integration tests (in addition to the unit tests)")

Và sau đó để thêm câu lệnh if vào đầu mỗi bài kiểm tra tích hợp:

if !*runIntegrationTests {
    this.T().Skip("To run this test, use: go test -integration")
}

Đây có phải là điều tốt nhất tôi có thể làm? Tôi đã tìm kiếm tài liệu làm chứng để xem có lẽ có quy ước đặt tên hoặc điều gì đó thực hiện điều này cho tôi, nhưng không tìm thấy bất cứ điều gì. Tui bỏ lỡ điều gì vậy?


2
Tôi nghĩ rằng stdlib sử dụng -short để vô hiệu hóa các thử nghiệm tấn công mạng (và cả những thứ lâu dài khác nữa). Giải pháp khôn ngoan khác của bạn có vẻ ổn.
Volker

-short là một lựa chọn tốt, cũng như các cờ xây dựng tùy chỉnh của bạn, nhưng cờ của bạn không cần phải ở trong chính. nếu bạn xác định var var integration = flag.Bool("integration", true, "Enable integration testing.")bên ngoài một hàm, biến sẽ hiển thị trong phạm vi gói và cờ sẽ hoạt động bình thường
Atifm

Câu trả lời:


155

@ Ainar-G đề xuất một số mẫu tuyệt vời để phân tách các bài kiểm tra.

Tập hợp các phương pháp Go này từ SoundCloud khuyên bạn nên sử dụng các thẻ xây dựng ( được mô tả trong phần "Các Ràng buộc Xây dựng" của gói xây dựng ) để chọn các thử nghiệm sẽ chạy:

Viết một integration_test.go và đặt cho nó một thẻ tích hợp xây dựng. Xác định cờ (toàn cầu) cho những thứ như địa chỉ dịch vụ và kết nối các chuỗi và sử dụng chúng trong các bài kiểm tra của bạn.

// +build integration

var fooAddr = flag.String(...)

func TestToo(t *testing.T) {
    f, err := foo.Connect(*fooAddr)
    // ...
}

go test lấy thẻ xây dựng giống như đi xây dựng, vì vậy bạn có thể gọi go test -tags=integration. Nó cũng tổng hợp một gói chính gọi cờ.Parse, vì vậy bất kỳ cờ nào được khai báo và hiển thị sẽ được xử lý và có sẵn cho các bài kiểm tra của bạn.

Như một tùy chọn tương tự, bạn cũng có thể chạy các thử nghiệm tích hợp theo mặc định bằng cách sử dụng điều kiện xây dựng // +build !unit, sau đó vô hiệu hóa chúng theo yêu cầu bằng cách chạy go test -tags=unit.

@adamc bình luận:

Đối với bất kỳ ai khác đang cố gắng sử dụng thẻ xây dựng, điều quan trọng là // +build testnhận xét là dòng đầu tiên trong tệp của bạn và bạn phải bao gồm một dòng trống sau nhận xét, nếu không -tagslệnh sẽ bỏ qua chỉ thị.

Ngoài ra, thẻ được sử dụng trong nhận xét bản dựng không được có dấu gạch ngang, mặc dù cho phép dấu gạch dưới. Ví dụ, // +build unit-testssẽ không hoạt động, trong khi // +build unit_testssẽ.


1
Tôi đã sử dụng điều này một thời gian và cho đến nay nó là cách tiếp cận hợp lý và đơn giản nhất.
Ban nhạc Ory

1
nếu bạn có kiểm tra đơn vị trong cùng một gói, bạn cần thiết lập // + build unittrong các thử nghiệm đơn vị và sử dụng đơn vị -tag cho chạy thử nghiệm
LeoCBS

2
@ Tyler.z.yang bạn có thể cung cấp liên kết hoặc thông tin chi tiết về việc ngừng sử dụng thẻ không? Tôi không tìm thấy thông tin như vậy. Tôi đang sử dụng các thẻ với go1.8 cho cách được mô tả trong câu trả lời và cả các loại và chức năng chế nhạo trong các bài kiểm tra. Nó là sự thay thế tốt cho các giao diện mà tôi nghĩ.
Alexander I.Grafov

2
Đối với bất kỳ ai khác đang cố gắng sử dụng thẻ xây dựng, điều quan trọng là // +buildnhận xét thử nghiệm là dòng đầu tiên trong tệp của bạn và bạn phải bao gồm một dòng trống sau nhận xét, nếu không -tagslệnh sẽ bỏ qua chỉ thị. Ngoài ra, thẻ được sử dụng trong nhận xét bản dựng không được có dấu gạch ngang, mặc dù cho phép dấu gạch dưới. Ví dụ: // +build unit-testssẽ không hoạt động, trong khi // +build unit_testssẽ
adamc

6
Làm thế nào để xử lý các ký tự đại diện? go test -tags=integration ./...doesnt làm việc, nó bỏ qua thẻ
Erika Dsouza

53

Để giải thích rõ hơn nhận xét của tôi cho câu trả lời xuất sắc của @ Ainar-G, trong năm qua, tôi đã sử dụng kết hợp -shortvới Integrationquy ước đặt tên để đạt được điều tốt nhất của cả hai thế giới.

Kiểm tra sự hài hòa giữa Unit và Integration, trong cùng một tệp

Cờ xây dựng trước đó buộc tôi phải có nhiều file ( services_test.go, services_integration_test.go, vv).

Thay vào đó, hãy lấy ví dụ này bên dưới, trong đó hai phần đầu là bài kiểm tra đơn vị và tôi có bài kiểm tra tích hợp ở cuối:

package services

import "testing"

func TestServiceFunc(t *testing.T) {
    t.Parallel()
    ...
}

func TestInvalidServiceFunc3(t *testing.T) {
    t.Parallel()
    ...
}

func TestPostgresVersionIntegration(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping integration test")
    }
    ...
}

Lưu ý rằng bài kiểm tra cuối cùng có quy ước là:

  1. bằng cách sử dụng Integrationtrong tên thử nghiệm.
  2. kiểm tra xem có chạy theo -shortchỉ thị cờ hay không.

Về cơ bản, thông số kỹ thuật là: "viết tất cả các bài kiểm tra một cách bình thường. Nếu đó là một bài kiểm tra dài hạn hoặc một bài kiểm tra tích hợp, hãy tuân theo quy ước đặt tên này và kiểm tra xem -shortnó có tốt với đồng nghiệp của bạn không."

Chỉ chạy các bài kiểm tra Đơn vị:

go test -v -short

điều này cung cấp cho bạn một tập hợp các thông điệp đẹp như:

=== RUN   TestPostgresVersionIntegration
--- SKIP: TestPostgresVersionIntegration (0.00s)
        service_test.go:138: skipping integration test

Chỉ chạy Kiểm tra tích hợp:

go test -run Integration

Điều này chỉ chạy các bài kiểm tra tích hợp. Hữu ích cho chim hoàng yến thử nghiệm khói trong sản xuất.

Rõ ràng nhược điểm của phương pháp này là nếu bất kỳ ai chạy go testmà không có-short cờ, nó sẽ mặc định chạy tất cả các bài kiểm tra - bài kiểm tra đơn vị và tích hợp.

Trong thực tế, nếu dự án của bạn đủ lớn để có các bài kiểm tra đơn vị và tích hợp, thì rất có thể bạn đang sử dụng một Makefilenơi bạn có thể có các lệnh đơn giản để sử dụng go test -shorttrong đó. Hoặc, chỉ cần đưa nó vào README.mdhồ sơ của bạn và gọi nó là ngày.


3
yêu sự đơn giản
Jacob Stanley

Bạn có tạo gói riêng cho thử nghiệm như vậy để chỉ truy cập vào các phần chung của gói không? Hay tất cả hỗn hợp?
Dr.eel

@ Dr.eel Vâng, đó là OT từ câu trả lời. Nhưng về mặt cá nhân, tôi thích cả hai hơn: một tên gói khác cho các bài kiểm tra để tôi có thể importgói và kiểm tra nó, điều này kết thúc cho tôi thấy API của tôi trông như thế nào với những người khác. Sau đó, tôi theo dõi bất kỳ logic nào còn lại cần được bao hàm dưới dạng tên gói thử nghiệm nội bộ.
eduncan911

@ eduncan911 Cảm ơn câu trả lời! Vì vậy, theo tôi hiểu ở đây package servicescó chứa một sute kiểm tra tích hợp, vì vậy để kiểm tra APIfo, gói này như một hộp đen, chúng ta nên đặt tên nó theo cách khác, package services_integration_testnó sẽ không cho chúng ta cơ hội làm việc với các cấu trúc bên trong. Vì vậy, gói cho các bài kiểm tra đơn vị (truy cập nội bộ) nên được đặt tên package services. Có phải vậy không?
Dr.eel

Đúng vậy. Đây là một ví dụ rõ ràng về cách tôi làm điều đó: github.com/eduncan911/podcast (thông báo mức độ phủ mã 100%, sử dụng Ví dụ)
eduncan911

50

Tôi thấy ba giải pháp khả thi. Đầu tiên là sử dụng chế độ ngắn cho các bài kiểm tra đơn vị. Vì vậy, bạn sẽ sử dụng go test -shortvới các bài kiểm tra đơn vị và tương tự nhưng không có-short cờ để chạy các bài kiểm tra tích hợp của bạn. Thư viện tiêu chuẩn sử dụng chế độ ngắn để bỏ qua các bài kiểm tra dài hạn hoặc làm cho chúng chạy nhanh hơn bằng cách cung cấp dữ liệu đơn giản hơn.

Thứ hai là sử dụng một quy ước và gọi các bài kiểm tra của bạn TestUnitFoohoặc TestIntegrationFoosau đó sử dụng -runcờ kiểm tra để biểu thị những bài kiểm tra nào sẽ chạy. Vì vậy, bạn sẽ sử dụng go test -run 'Unit'cho các bài kiểm tra đơn vị và go test -run 'Integration'cho các bài kiểm tra tích hợp.

Tùy chọn thứ ba là sử dụng một biến môi trường và đưa nó vào thiết lập thử nghiệm của bạn với os.Getenv. Sau đó, bạn sẽ sử dụng đơn giản go testcho các bài kiểm tra đơn vị và FOO_TEST_INTEGRATION=true go testcác bài kiểm tra tích hợp.

Cá nhân tôi thích -shortgiải pháp hơn vì nó đơn giản hơn và được sử dụng trong thư viện tiêu chuẩn, vì vậy có vẻ như đó là một cách thực tế để tách / đơn giản hóa các bài kiểm tra chạy dài. Nhưng các giải pháp -runos.Getenvgiải pháp cung cấp tính linh hoạt hơn (cũng cần thận trọng hơn, vì regexps có liên quan -run).


1
lưu ý rằng những người chạy thử nghiệm cộng đồng (ví dụ Tester-Go) phổ biến cho IDE (Atom, Sublime, v.v.) có tùy chọn tích hợp để chạy với -shortcờ, cùng với -coveragevà những người khác. do đó, tôi sử dụng kết hợp cả Tích hợp trong tên thử nghiệm, cùng với các if testing.Short()kiểm tra trong các thử nghiệm đó. nó cho phép tôi để có sản phẩm tốt nhất của cả hai thế giới: chạy với -shorttrong IDE, và chạy một cách rõ ràng chỉ kiểm tra tích hợp vớigo test -run "Integration"
eduncan911

5

Tôi đã cố gắng tìm một giải pháp cho điều tương tự gần đây. Đây là những tiêu chí của tôi:

  • Giải pháp phải phổ biến
  • Không có gói riêng biệt cho các bài kiểm tra tích hợp
  • Việc tách nên đầy đủ (tôi sẽ có thể chạy thử nghiệm tích hợp duy nhất )
  • Không có quy ước đặt tên đặc biệt nào cho các bài kiểm tra tích hợp
  • Nó sẽ hoạt động tốt mà không cần công cụ bổ sung

Các giải pháp nói trên (cờ tùy chỉnh, thẻ xây dựng tùy chỉnh, biến môi trường) không thực sự đáp ứng tất cả các tiêu chí trên, vì vậy sau khi tìm hiểu và chơi một chút, tôi đã đưa ra giải pháp này:

package main

import (
    "flag"
    "regexp"
    "testing"
)

func TestIntegration(t *testing.T) {
    if m := flag.Lookup("test.run").Value.String(); m == "" || !regexp.MustCompile(m).MatchString(t.Name()) {
        t.Skip("skipping as execution was not requested explicitly using go test -run")
    }

    t.Parallel()

    t.Run("HelloWorld", testHelloWorld)
    t.Run("SayHello", testSayHello)
}

Việc thực hiện rất đơn giản và tối thiểu. Mặc dù nó yêu cầu một quy ước đơn giản cho các bài kiểm tra, nhưng nó ít bị lỗi hơn. Cải tiến hơn nữa có thể là xuất mã sang chức năng trợ giúp.

Sử dụng

Chỉ chạy thử nghiệm tích hợp trên tất cả các gói trong một dự án:

go test -v ./... -run ^TestIntegration$

Chạy tất cả các thử nghiệm ( thường xuyên và tích hợp):

go test -v ./... -run .\*

Chỉ chạy các bài kiểm tra thông thường :

go test -v ./...

Giải pháp này hoạt động tốt mà không cần công cụ, nhưng Makefile hoặc một số bí danh có thể giúp người dùng dễ dàng hơn. Nó cũng có thể được tích hợp dễ dàng vào bất kỳ IDE nào hỗ trợ chạy thử nghiệm.

Ví dụ đầy đủ có thể được tìm thấy tại đây: https://github.com/sagikazarmark/modern-go-application

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.