Tôi nên kiểm tra đơn vị gì trong Laravel?

Sự khác biệt giữa kiểm tra đơn vị, kiểm tra tích hợp và kiểm tra tính năng là gì?

Trước hết, hãy nói về định nghĩa của các thuật ngữ này. Chúng tôi sẽ tập trung vào bốn thuật ngữ. kiểm tra đơn vị, kiểm tra tích hợp, kiểm tra tính năng và kiểm tra bên ngoài. Thậm chí còn nhiều hơn nữa [thử nghiệm khói, thử nghiệm e2e, thử nghiệm hoàng hôn, v.v…] nhưng chúng tôi sẽ tập trung vào những điều này vì chúng rất phù hợp với hệ sinh thái Laravel của chúng tôi

Tất cả những điều này được gọi chung là bài kiểm tra đơn vị. Thực sự, một cách chính xác hơn để đề cập đến chúng sẽ là các bài kiểm tra tự động. Ý tưởng là chúng tôi viết mã để tự động kiểm tra mã khác. Vì vậy, hãy chia nhỏ các loại mà tôi sử dụng trong dự án Laravel của mình

Bài kiểm tra đơn vị

Một bài kiểm tra đơn vị là hình thức kiểm tra đơn giản nhất. Bạn viết một bài kiểm tra lấy phần nhỏ nhất của một đoạn mã và kiểm tra kết quả từ các đầu vào có thể. Nghĩ về kích thước của một phương thức trên một lớp - hoặc một hàm đơn giản. Một dự án Laravel mặc định thậm chí còn cung cấp một thư mục và ví dụ kiểm tra cho những dự án này. Mặc định của Laravel ExampleTest trong thư mục tests/Unit thậm chí không tải toàn bộ nhân ứng dụng. Nó nhằm truyền đạt rằng đây là những hình thức thử nghiệm đơn giản nhất

Một cách để xem xét vấn đề này là bạn chỉ nên kiểm tra phần nhỏ nhất của mã hoặc thứ đưa ra một quyết định đại khái, tối đa. Tốt nhất là bạn đang viết các phương thức của mình một cách đơn giản, vì vậy điều này thật dễ dàng. [Trên thực tế, kiểm thử đơn vị phù hợp thực sự giúp bạn viết mã sạch hơn - vì mã sạch là cần thiết để kiểm tra đơn vị đơn giản hơn, nhanh hơn. ] Hãy xem một ví dụ trong đó mô hình người dùng Laravel mặc định của chúng tôi có họ và tên, và chúng tôi muốn lấy tên hiển thị - là tên và chữ cái đầu cuối cùng của họ

class User extends Authenticatable
{
  public function getDisplayName[]: string
  {
    return trim[$this->first_name . ' ' . substr[[string] $this->last_name, 0, 1]];
  }
  // snip

Các bài kiểm tra đơn vị của chúng tôi sẽ là phiên bản đơn giản nhất của điều này. Tôi có thể nghĩ ra một vài trường hợp… Tôi muốn kiểm tra đầu ra của $user->getDisplayName[] khi…

  • first_name không trống, last_name có nhiều hơn một ký tự
  • first_name không trống, last_name có 1 ký tự
  • first_name không trống, last_ name trống
  • lặp lại tất cả những điều này với first_name trống

Chúng là những bài kiểm tra rất đơn giản với số lượng mã nhỏ nhất có thể

Như tôi đã đề cập, trong triển khai mặc định của thư mục kiểm tra đơn vị Laravel, nó thậm chí không triển khai đặc điểm CreatesApplication. Và có lẽ đó là kịch bản mặc định tốt nhất. Tuy nhiên, trong cuộc sống thực, tôi có xu hướng làm mờ các dòng một chút. Mình sẽ test đoạn code nhỏ nhất thỉnh thoảng cũng dùng Laravel framework. [Tôi tin rằng Laravel hoạt động như mong đợi vì nó đã được kiểm tra đúng cách, vì vậy tôi có thể cho rằng khi tôi sử dụng nó, nó sẽ hoạt động như mong đợi]. Đôi khi, cần phải tải ứng dụng Laravel… chẳng hạn - giả sử tôi muốn kiểm tra một cổng mà tôi đã viết. Miễn là cổng đó không chạm vào cơ sở dữ liệu, điều này là ổn. nó có thể là một bài kiểm tra đơn vị

Đối với ví dụ này, giả sử chúng ta có một cổng xác minh xem người dùng có thể tìm kiếm kết quả dựa trên vị trí hay không. Ngay bây giờ, việc kiểm tra đó rất đơn giản - họ phải có mã zip. [Sau này có thể sẽ dựa vào kế hoạch của bọn họ, một số thứ khác, ai biết được. ]. Chúng tôi làm cổng sau

Gate::define['can-search-location-based', function [User $user] {
  return $user->zipcode !== null;
}];

Đây là một nơi khác mà tôi có thể viết một bài kiểm tra đơn vị nhỏ. Về mặt kỹ thuật, nó yêu cầu chúng tôi tải ứng dụng Laravel, nhưng điều đó không yêu cầu bất kỳ thứ gì tích hợp ở mọi nơi - nó chỉ nặng hơn. Vì vậy, bài kiểm tra đơn vị của chúng tôi sẽ như thế nào?

public function testUserCanSearchLocationBased[]: void
{
  $user = new App\Models\User[];
  $user->zipcode = '12345';
  
  self::assertTrue[$user->can['can-search-location-based']];

Hãy xem, chỉ là một bài kiểm tra nhỏ nhanh - vì vậy nó là một đơn vị

Bây giờ, nếu Cổng đó yêu cầu chúng tôi truy vấn cơ sở dữ liệu thì sao?

Bài kiểm tra tích hợp

Tôi xác định thử nghiệm tích hợp là tích hợp hai bộ ngữ cảnh riêng biệt. Bối cảnh đối với tôi có nghĩa là một trong các mã của chúng tôi, cơ sở dữ liệu của chúng tôi, redis của chúng tôi, v.v… “Khái niệm” này không xuất hiện trong bản cài đặt dự án Laravel tiêu chuẩn

Trong thực tế, thông thường đây chỉ là thứ kết nối với cơ sở dữ liệu, nhưng không thực thi toàn bộ luồng người dùng. Đó là một nền tảng trung gian. Ví dụ: bạn có thể có một điểm cuối truy vấn 4 loại phạm vi Eloquent khác nhau, xử lý đầu vào của người dùng và hiển thị đầu ra. Đó là quá nhiều để tích hợp - đó là một thử nghiệm tính năng. Nhưng hãy nghĩ về tất cả các thiết lập cần thiết để kiểm tra điều đó. Có thể mỗi phạm vi yêu cầu hàng tấn mục nhập cơ sở dữ liệu khác nhau được tạo và nhiều dịch vụ được mô phỏng. Điều đó tốn nhiều công sức hơn và mất nhiều thời gian chạy hơn cho các bài kiểm tra

Vì điều này, tôi sử dụng các bài kiểm tra tích hợp. Đây là những bài kiểm tra trình độ trung bình và đôi khi rất chi tiết của bạn. Hãy xem Cổng được cập nhật của chúng tôi làm ví dụ

Gate::define['can-search-location-based', function [User $user] {
  return $user->subscriptions[]->contains[Subscription::PREMIUM];
}];

Bây giờ, cổng của chúng tôi đang thực hiện một số loại truy vấn đối với cơ sở dữ liệu để xác định xem người dùng này có đăng ký trả phí hay không. Bây giờ chúng tôi chuyển sang kiểm tra tích hợp, vì vậy chúng tôi có thể khởi động ứng dụng và sử dụng các nhà máy để tạo dữ liệu. [Lưu ý rằng chúng tôi không kiểm tra đầy đủ điểm cuối]. Chúng tôi thử nghiệm các tích hợp này với tất cả các kịch bản mà chúng tôi có thể nghĩ ra vì chúng chạy nhanh hơn [ít thiết lập hơn]. chúng tôi có thể kiểm tra

  • người dùng đang thử nghiệm không có đăng ký. sai
  • người dùng đang thử nghiệm có đăng ký cao cấp. ĐÚNG VẬY
  • người dùng đang thử nghiệm có đăng ký cao cấp và đăng ký cơ bản. ĐÚNG VẬY
  • người dùng đang thử nghiệm có đăng ký cơ bản. sai
  • người dùng đang thử nghiệm có đăng ký cơ bản, người dùng thứ hai trong db có đăng ký cao cấp. sai

Dấu đầu dòng cuối cùng đó là một ví dụ về những thứ cần được kiểm tra [chúng ta có chắc là mình đang nhắm mục tiêu đúng người dùng không? Điều gì sẽ xảy ra nếu cổng của chúng ta chỉ tìm kiếm người dùng đầu tiên hoặc bất kỳ người dùng nào với phí bảo hiểm] nhưng thực sự khó thiết lập . Tại sao tôi cần thực hiện tìm kiếm thông qua một điểm cuối đầy đủ với dữ liệu đã xử lý, trong khi thực sự tôi chỉ muốn kiểm tra một phiên bản logic của cổng

Dù sao, mục đích của kiểm tra tích hợp là kiểm tra tích hợp mã của chúng tôi với một ngữ cảnh khác. Thông thường, tôi thấy đây là lớp cơ sở dữ liệu. Tôi sẽ kiểm tra phạm vi, cổng, dịch vụ nhóm nhiều hành động lại với nhau và hơn thế nữa. Tôi sẽ không sử dụng toàn bộ đầu vào do người dùng cung cấp [hoặc mô phỏng] và đo lường đầu ra từ ứng dụng. Đó là một thử nghiệm tính năng

Kiểm tra tính năng

Kiểm tra tính năng là một cách để nói kiểm tra đầy đủ từ đầu đến cuối trong ngữ cảnh mã PHP của chúng tôi. Với URL này, trạng thái người dùng, trạng thái ứng dụng và đầu vào của người dùng, tôi nhận được đầu ra nào với những tác dụng phụ nào? . [Đó là một cái gì đó khác biệt, nằm ngoài phạm vi của bài viết này. ]. Bạn đang thử nghiệm một điểm cuối API hoặc phương pháp điều khiển hoặc lệnh điều khiển một lần duy nhất

Laravel cung cấp một thư mục kiểm tra tính năng với một bài kiểm tra ví dụ. Nó cho thấy cách bạn có thể tải một URL và xử lý phản hồi của nó

Kiểm tra tính năng là một nơi tuyệt vời để thiết lập trạng thái người dùng và ứng dụng thực tế, đồng thời chuyển dữ liệu và kiểm tra kết quả. Lợi ích của công cụ chúng tôi có trong Laravel là chúng tôi có thể sử dụng các nhà máy để thiết lập trạng thái dữ liệu - chúng tôi không phải xử lý tất cả những điều này thông qua các lệnh gọi điểm cuối trên ứng dụng của mình

Hãy chia nhỏ những gì tôi có thể kiểm tra trên điểm cuối của cửa hàng cho một mục blog. Mục blog được tạo bởi người dùng đã đăng nhập, nó có tiêu đề bắt buộc nhưng không được dài hơn 255 ký tự và phần nội dung bắt buộc phải có và không được dài hơn 65535 ký tự. Tôi sẽ viết các bài kiểm tra riêng biệt, duy nhất cho từng gạch đầu dòng sau

  • bài đăng trái phép của người dùng lên /store nhận 403
  • người dùng được ủy quyền đăng bài lên /store
    • không có trường nào thất bại với 422
    • trường tiêu đề và nội dung chuỗi rỗng không thành công với 422
    • trường tiêu đề 256 ký tự, trường nội dung 65536 ký tự, không thành công với 422
    • trường tiêu đề và trường nội dung được đặt làm giá trị mảng không thành công 422
    • trường tiêu đề 'the-title' trường nội dung 'phần thân ở đây' thành công, kiểm tra mục nhập blog mới với các giá trị đó xuất hiện được liên kết với người dùng hiện tại dưới dạng mô hình

Đó chỉ là những điều cơ bản của một bài kiểm tra tính năng. Bây giờ, bạn có thể lặp lại thao tác này để chỉnh sửa [bao gồm cả việc kiểm tra quyền chỉnh sửa và đảm bảo rằng nó sẽ ghi đè lên mục blog phù hợp. ]

mẹo thú vị. Đôi khi…hiếm khi…Tôi viết một bộ thử nghiệm và chúng chạy thành công ngay lập tức. Phần lớn, thậm chí sau ngần ấy năm, điều đó làm tôi phát điên. Vì vậy, tôi truy cập mã của mình và chỉnh sửa cụ thể [tạm thời] để thử nghiệm của tôi không thành công và chạy lại thử nghiệm. Nếu họ tiếp tục vượt qua, tôi biết bài kiểm tra của tôi đang nhắm mục tiêu sai. Nếu họ thất bại, thì tôi biết tôi chỉ "tốt" một lần này

Các bài kiểm tra tính năng cũng có thể thực hiện những việc tích hợp với API của bên thứ ba hoặc có các mối quan hệ phức tạp giữa các mô hình hùng biện. Một trong những lợi ích của lớp kiểm tra tích hợp cho các mô hình hùng hồn là chúng tôi đã kiểm tra kỹ lưỡng chức năng của chúng [và nhanh hơn nhiều]. Do đó, chúng ta chỉ cần viết các bài kiểm tra Tính năng đủ duy nhất để chỉ ra rằng các phạm vi phù hợp đó đang được gọi. Nếu chúng tôi biết điều đó, chúng tôi có thể giả định một cách hợp lý [không phải luôn luôn, nhưng hợp lý] rằng các bài kiểm tra tích hợp sau đó sẽ bao gồm các trường hợp duy nhất và cạnh

Khi nói đến bên thứ ba, các bài kiểm tra tính năng sẽ loại bỏ chúng. Nói chung, tôi có xu hướng kết thúc cuộc gọi của bên thứ ba trong một dịch vụ [ngay cả khi tôi đang sử dụng mặt tiền HTTP có thể chế nhạo của Laravel. ]. Sau đó, điều này được đưa vào bằng cách tiêm phụ thuộc của Laravel. Điều này cho phép tôi chế nhạo nó. Giả sử rằng dịch vụ của chúng ta có một phương thức gọi là

Gate::define['can-search-location-based', function [User $user] {
  return $user->zipcode !== null;
}];
0 nhận vào một chuỗi, truy vấn FooDotCom và trả về một đối tượng kết quả. Điều đó có một khóa gọi là
Gate::define['can-search-location-based', function [User $user] {
  return $user->zipcode !== null;
}];
1 có số lượng kết quả. Cuối cùng, tôi kiểm tra xem liệu tôi có nên hiển thị kết quả hay hiển thị thông báo cho biết không tìm thấy gì không

Tôi có thể có một bài kiểm tra như thế này

public function testFooDotComReturnsNothing[]: void
{
  $term = 'what??'
  
  $this->mock[FooDotComService::class, function [$mock] use [$term] {
    $results = [object] ['results' => 0];
    $mock->shouldReceive['executeSearch']->once[]->with[$term]->andReturn[$results];
  }];

  $response = $this->get[route['search-foo', ['q' => $term]]];
  $response->assertOk[];
  $response->assertSeeText['There were no results.'];
}

Ở đây chúng tôi tìm kiếm thuật ngữ

Gate::define['can-search-location-based', function [User $user] {
  return $user->zipcode !== null;
}];
2 và yêu cầu dịch vụ giả vờ rằng nó đã truy vấn dịch vụ bên ngoài. Chúng tôi mô phỏng kết quả và sau đó kiểm tra cách mã của chúng tôi xử lý kết quả của bên thứ ba đó. Chúng tôi không thực sự tấn công bên thứ ba

Bạn vẫn cần kiểm tra xem các phần phụ thuộc bên thứ ba hoặc bên ngoài của bạn có đang hoạt động như mong đợi không. Tuy nhiên, bạn không cần phải làm điều này quá thường xuyên so với tần suất bạn nên chạy phần còn lại của bài kiểm tra đơn vị của mình. Trên thực tế, theo ý kiến ​​​​của tôi, các bài kiểm tra đơn vị cốt lõi của bạn sẽ không bao giờ đánh vào các dịch vụ bên ngoài. [Tôi có xu hướng định cấu hình các biến môi trường PHPUnit trong tệp xml thành các giá trị không thành công, để nó không vô tình tải thông tin đăng nhập hộp cát của môi trường cục bộ của tôi và chạm vào điểm cuối mà tôi không biết. ] Đó là lý do tại sao chúng tôi có một bộ riêng gọi là bên ngoài

Kiểm tra bên ngoài

Theo dõi dịch vụ bên ngoài FooDotCom của chúng tôi, hãy nói về việc thực sự thực hiện tìm kiếm. Tôi có xu hướng tạo một thư mục riêng, một tệp

Gate::define['can-search-location-based', function [User $user] {
  return $user->zipcode !== null;
}];
3 riêng với các biến môi trường phù hợp, nhưng sử dụng cùng một khuôn khổ như một trường hợp thử nghiệm tính năng. Theo mặc định, điều này sẽ không chạy mọi lúc. Tuy nhiên, bằng cách sử dụng các công cụ của Laravel, chúng ta vẫn có thể đạt được hầu hết những thứ chúng ta cần một cách dễ dàng. Đây là nơi chúng tôi sẽ kiểm tra các phương pháp khác nhau về cách dịch vụ của chúng tôi tấn công bên thứ ba bên ngoài. Tôi chỉ có thể chạy điều này trên tích hợp liên tục nhánh
Gate::define['can-search-location-based', function [User $user] {
  return $user->zipcode !== null;
}];
4 - ngay trước khi tôi triển khai - để đảm bảo rằng bên thứ ba của tôi không thay đổi đối với tôi

Với ví dụ của chúng tôi, tôi có thể chỉ có ba thử nghiệm bên ngoài cho dịch vụ của chúng tôi

  • gửi dữ liệu tìm kiếm không hợp lệ vào dịch vụ [như một chuỗi trống] trả về một đối tượng kết quả có khóa kết quả bằng 0
  • tìm kiếm một thuật ngữ mà chúng tôi biết là không bao giờ có thể tìm thấy, sẽ không có kết quả
  • tìm kiếm một cụm từ mà chúng tôi biết sẽ luôn được tìm thấy [hy vọng với số lượng kết quả có thể dự đoán được, nhưng chắc chắn là trên 0]

Những điều này sẽ được thực hiện bằng cách thực sự yêu cầu dịch vụ, sự kết hợp giữa các biến môi trường PHPUnit và nội xạ phụ thuộc Laravel của chúng tôi có thể thực hiện được. Ví dụ

public function testExecuteSearchFindsResults[]: void
{
  $service = app[]->make[FooDotComService::class];
  $responseObject = $service->executeSearch['blueberries'];
  self::assertIsObject[$responseObject];
  self::assertGreaterThan[0, $responseObject->results];
}

Ghi chú kết thúc

Điểm chia nhỏ các bài kiểm tra này thành các loại khác nhau là gấp đôi

Đầu tiên, nó cho phép chúng tôi suy nghĩ rõ ràng về các loại thử nghiệm và cách chia mã của chúng tôi thành các khối đơn giản hơn. Để viết bài kiểm tra đơn vị tốt, bạn phải viết đơn vị mã tốt. Để có các bài kiểm tra tích hợp để kiểm tra các tình huống cụ thể, bạn phải suy nghĩ ở dạng phạm vi có thể lặp lại, không phải truy vấn nội tuyến. Để viết các bài kiểm tra tính năng tốt, bạn phải có các dịch vụ để mô phỏng, do đó giảm sự liên kết của bên thứ ba

Thứ hai, nó có thể giúp bộ thử nghiệm của chúng tôi chạy nhanh hơn về tổng thể. Kiểm tra tính năng luôn mất nhiều thời gian hơn để chạy vì chúng phải tải toàn bộ môi trường, chạy phần mềm trung gian, xử lý đầu vào, gọi cơ sở dữ liệu, xử lý đầu ra và hoàn thiện ứng dụng. Các bài kiểm tra tích hợp luôn mất nhiều thời gian hơn đơn vị vì chúng đánh vào cơ sở dữ liệu và trong khi cơ sở dữ liệu và giao dịch nhanh, chúng không nhanh bằng mã PHP đơn giản. Các bài kiểm tra đơn vị là nhanh nhất vì chúng chỉ nên kiểm tra một vài đoạn mã nhỏ và các quyết định. Các thử nghiệm bên ngoài mất nhiều thời gian nhất vì chúng đang tấn công bên thứ ba. Chúng tôi chắc chắn rằng bên thứ ba tôn trọng hợp đồng giữ mọi thứ như cũ, vì vậy chúng tôi không cần phải liên tục tấn công họ

Đây chỉ là kinh nghiệm của tôi sau 20 năm lập trình và hơn một thập kỷ thực sự sử dụng các bài kiểm tra đơn vị. Điều đó không có nghĩa đó là cách duy nhất, nhưng hy vọng nó sẽ truyền cảm hứng cho bạn suy nghĩ về cách bạn nên cấu trúc dự án của mình

Những gì nên được kiểm tra trong thử nghiệm đơn vị?

Kiểm tra đơn vị là một cách kiểm tra đơn vị - đoạn mã nhỏ nhất có thể được tách biệt một cách logic trong hệ thống. Trong hầu hết các ngôn ngữ lập trình, đó là hàm, chương trình con, phương thức hoặc thuộc tính . Phần biệt lập của định nghĩa là quan trọng.

Kiểm tra đơn vị trong Laravel là gì?

Các bài kiểm tra đơn vị là các bài kiểm tra tập trung vào một phần rất nhỏ, tách biệt trong mã của bạn . Trên thực tế, hầu hết các bài kiểm tra đơn vị có thể tập trung vào một phương pháp duy nhất. Các kiểm tra trong thư mục kiểm tra "Đơn vị" của bạn không khởi động ứng dụng Laravel của bạn và do đó không thể truy cập cơ sở dữ liệu của ứng dụng hoặc các dịch vụ khung khác.

Một bài kiểm tra đơn vị tốt trông như thế nào?

Điều gì tạo nên một bài kiểm tra đơn vị tốt? . Đây là những thực tiễn tốt nhất bạn muốn ghi nhớ để viết các bài kiểm tra đơn vị hiệu quả và đảm bảo thời gian đầu tư thực sự hữu ích. independent, repeatable, readable, exhaustive, realistic, maintainable, fast and easy. These are the best practices you want to keep in mind in order to write effective unit tests and make sure the time invested is truly useful.

Chủ Đề