Một trường hợp tạo mã Go: làm chứng

Nov 07 2022
Nếu bạn đã sử dụng Go được một thời gian, có thể bạn đã quen thuộc với thư viện thử nghiệm duỗi / làm chứng. Nó giúp bạn dễ dàng viết các bài kiểm tra và cung cấp một số hàm xác nhận, chẳng hạn như Bằng, Chứa, Lớn hơn và nhiều hàm khác.

Nếu bạn đã sử dụng Go được một thời gian, có thể bạn đã quen thuộc với thư viện thử nghiệm duỗi / làm chứng . Nó giúp bạn dễ dàng viết các bài kiểm tra và cung cấp một số hàm xác nhận, chẳng hạn như , Equalvà nhiều hàm khác .ContainsGreater

Các hàm xác nhận hoạt động khác nhau tùy thuộc vào phạm vi. Ví dụ, khi được gọi từ asserthoặc requiregói. Lỗi trước ghi lại lỗi và tiếp tục, trong khi lỗi sau sẽ dừng kiểm tra ngay lập tức. Cả hai gói đều cung cấp các chữ ký chức năng giống nhau. Assertions cũng được gọi là phương thức từ một suitecấu trúc, bao bọc *testing.Tcon trỏ. Và mỗi xác nhận có một đối định định dạng với một *fhậu tố và các tham số bổ sung ( msg string, params ...interface{}) ở cuối.

Điều này để lại cho chúng ta ba chiều của các hàm khẳng định: Hàm tĩnh, phương thức bộ và được định dạng. Tổng cộng có tám phiên bản khác nhau của mỗi chức năng :

Ví dụ về tất cả các triển khai của .Equal

Đây là một trường hợp thú vị cho việc tạo mã. Có vẻ như chúng ta có thể triển khai tất cả các chức năng dưới dạng trình bao bọc để triển khai xác nhận hợp quy. Nó chủ yếu là mã lặp đi lặp lại hoặc mã soạn sẵn sẽ rất tẻ nhạt nếu viết bằng tay. Và, không có gì đáng ngạc nhiên, đó chính xác là những gì mà gói này thực hiện.

Phần còn lại của bài đăng này là một quá trình cân não khi tôi xem qua mã, cố gắng hiểu cách thức hoạt động nội bộ của chứng thực và cách họ tổ chức mã của mình để sử dụng tạo mã.

Ví dụ: Equalhàm

Đầu tiên chúng ta hãy xem xét Equalchức năng và hành vi khác nhau như thế nào assertrequire:

Việc triển khai cơ bản của .Equallà giống nhau. So sánh hai giá trị và cung cấp đầu ra văn bản không được thay đổi asserthoặc requiretriển khai. Những gì thay đổi ở đây là quy trình của bài kiểm tra. Nó dừng lại hoặc ghi lại lỗi và tiếp tục.

Hàm assert.Equalnơi triển khai chính tắc của Equal.

Tôi sẽ dành thông tin chi tiết về cách so sánh hoạt động ngoài ObjectsAreEqualchức năng. Hãy tập trung vào mã xung quanh và cách nó được gọi ở những nơi khác trong cơ sở mã.

Một điều quan trọng cần lưu ý ở đây là các assert.*hàm trả về a bool. Điều này được tận dụng bởi require.*đối tác của họ.

require.Equaltrông chính xác những gì bạn mong đợi:

Nó bao bọc assert.Equalhàm và gọi t.FailNow()trong trường hợp xác nhận không thành công (trả về false).

Điều này cũng đúng cho mọi hàm khẳng định. Thay vì viết các chữ ký hàm soạn sẵn cho mỗi xác nhận, hãy xem cách họ sử dụng các mẫu để tạo các thân hàm.

Xác nhận các mẫu chức năng

yêu cầu gói

Mọi chức năng trong requiregói đều có cùng một mã soạn sẵn. Nó phải (1) gọi assert.*hàm; và (2) không kiểm tra được nếu khẳng định trả về sai.

{{.Comment}}
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
	if h, ok := t.(tHelper); ok { h.Helper() }
	if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) {
                return
        }
        t.FailNow()
}

Các phương pháp của Suite

Vì các xác nhận này là các phương thức trong cấu trúc bộ, chúng cần một bộ thu, sẽ thuộc loại *Assertiontrên cả hai .Assert().Require()cấu trúc.

{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) {
	if h, ok := a.t.(tHelper); ok { h.Helper() }
	{{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}

Họ chỉ sử dụng các mẫu hàm định dạng cho assertgói. Tất cả các định dạng khác được tạo dựa trên các assertchức năng gói, bao gồm cả những định dạng có tiền tố. Đây là lý do tại sao bạn sẽ chỉ tìm thấy một mẫu định dạng trong assertgói.

{{.CommentFormat}}
func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool {
	if h, ok := t.(tHelper); ok { h.Helper() }
	return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}})
}

Kết hợp ba mẫu này, bạn có thể tạo tất cả các bố cục chức năng được cung cấp bởi testify:

  1. Sử dụng *fmẫu để tạo các hàm định dạng dựa trên asserttriển khai chuẩn.
  2. Sử dụng requiremẫu để tạo các chức năng của trình bao bọc assert.
  3. Sử dụng mẫu phương pháp bộ để tạo các chức năng với *Assertionsbộ thu kết thúc các cuộc gọi đến assert.*require.*các chức năng.

Có một tệp trình tạo duy nhất trong _codegenthư mục, được sử dụng bởi tất cả các loại chức năng. Nó được gọi từ mỗi gói bằng cách sử dụng các tham số cờ. Ví dụ, lệnh được sử dụng để tạo require.*các hàm:

//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs"

Còn nhiều điều nữa về cách thức hoạt động của tập lệnh tạo mã, nhưng tôi không nghĩ rằng việc xem qua logic đó ở đây là hữu ích. Phần quan trọng nhất để hiểu cách tiếp cận của họ là xem cấu trúc này:

Phân tích cú pháp pkgtệp đầu vào (theo mặc định trỏ đến assert) sẽ xuất ra một phần testFunc. Mỗi một trong những phần tử này được sử dụng để tạo một hàm đối chiếu dựa trên một mẫu. Cấu trúc cung cấp một số hàm trợ giúp để xây dựng đối số và tham số:

Bạn có thể dễ dàng đoán họ làm gì dựa trên tên.

Việc tạo mã rất phức tạp, nhưng đó là một sự phức tạp tiềm ẩn không len lỏi vào phần còn lại của mã thư viện. Sau khi mã phân tích cú pháp được tạo, không chắc rằng nó cần phải được thay đổi.

Một cách tiếp cận thay thế

Tạo mã rất thú vị, nhưng thường xuyên hơn không khi tôi tự hỏi bản thân liệu nó có thực sự cần thiết hay không thì câu trả lời là không. Các thế hệ mã tránh viết nhiều dòng mã soạn sẵn. Nhưng nó để lại một hàm tạo phức tạp, thường chưa được kiểm tra, sử dụng một astlogic lộn xộn.

Bất cứ khi nào tôi cảm thấy cần phải sử dụng tạo mã, tôi tự hỏi mình liệu vấn đề tôi đang cố gắng giải quyết có phải là vấn đề nhân tạo hay không. Nếu đó là một vấn đề thực sự, nó có thể được giải quyết theo những cách khác không? Hãy áp dụng lý do này cho gói làm chứng:

Có ba lý do chính để tạo mã được sử dụng ở đây. Các chức năng khẳng định giống nhau có thể được cung cấp với các hương vị khác nhau:

  1. Hàm tĩnh so với các phương thức: cung cấp một hàm hoặc một phương thức trên cấu trúc bao bọc *testing.Tmột vấn đề nhân tạo. Một cách tiếp cận cố chấp sẽ là chỉ cung cấp các phương pháp.
  2. Có định dạng và không có định dạng: đây có vẻ như là một vấn đề hợp pháp, nhưng có giải pháp khác thay vì đưa ra một hậu tố hàm khác.
  3. Không nhanh chóng: cũng là một tính năng hợp pháp, nhưng có thể được giải quyết bằng các cấu trúc đơn giản hơn là cung cấp các chức năng theo phạm vi gói khác nhau.

Như một bài tập suy nghĩ, đây là một giải pháp thay thế có thể được xây dựng mà không cần tạo mã:

Nếu chúng tôi chấp nhận rằng (1) là một vấn đề nhân tạo và chỉ cung cấp các phương pháp, bạn có thể thiết kế các xác nhận bằng cách sử dụng một mẫu trình tạo. Assertions thuộc về một assertioncấu trúc có chức năng định cấu hình cho hành vi luồng điều khiển đã thiết lập đó.

Đây là nguyên mẫu của cấu trúc xác nhận tuân thủ giao diện đó và giữ các thuộc tính định dạng và luồng như một phần của ngữ cảnh thử nghiệm:

Điều đáng nói là đây không phải là một triển khai nghiêm túc, đây là một bài tập suy nghĩ về cách cấu trúc điều này . Ở đây còn thiếu các tính năng như cung cấp hỗ trợ cho *testing.Bcác cuộc gọi và có thể là các trường hợp sử dụng khác mà tôi không thể xem ngay bây giờ.

Đánh đổi

Tôi nghĩ sự đánh đổi lớn nhất ở đây là: độ phức tạp chuyển từ testifygói sang mã thử nghiệm khách hàng. Viết bài kiểm tra bằng cách sử dụng làm chứng là cực kỳ đơn giản. Tôi hiếm khi mở tài liệu của họ sau vài lần đầu tiên tôi viết thử nghiệm. Đây là một trải nghiệm độc đáo so với các thư viện thử nghiệm khác. Ví dụ với chai JavaScript, nơi tôi thường quên thứ tự và những cách thành ngữ mà tôi nên viết khẳng định.

Đây là một đặc điểm tuyệt vời của làm chứng. Thư viện tốt là những thư viện bạn không phải suy nghĩ quá nhiều về chúng và chúng chỉ hoạt động .

Đường dẫn tạo mã để lại gánh nặng cho nhà phát triển thư viện trong việc duy trì, nhưng nó có thể đơn giản hóa API gói, giúp việc áp dụng nhanh hơn. Tôi nghĩ rằng testifycác nhà phát triển có lẽ đã gọi đúng ở đây khi làm cho điều này đơn giản hơn cho người dùng, ngay cả khi nó có nguy cơ làm phức tạp khả năng bảo trì của công cụ của họ.

Rốt cuộc, tốt hơn là có tất cả sự phức tạp ở một nơi duy nhất hơn là một chút phức tạp ở khắp mọi nơi.

© Copyright 2021 - 2022 | vngogo.com | All Rights Reserved