Kiểm tra trong sự cô lập
Khi phát triển một plugin, cách tốt nhất để kiểm tra nó là không tải môi trường WordPress.
Nếu bạn viết mã có thể dễ dàng kiểm tra mà không cần WordPress, mã của bạn sẽ trở nên tốt hơn .
Mọi thành phần được kiểm tra đơn vị, nên được kiểm tra riêng rẽ : khi bạn kiểm tra một lớp, bạn chỉ phải kiểm tra lớp cụ thể đó, giả sử tất cả các mã khác đang hoạt động hoàn hảo.
Đây là lý do tại sao các bài kiểm tra đơn vị được gọi là "đơn vị".
Là một lợi ích bổ sung, không cần tải lõi, bài kiểm tra của bạn sẽ chạy nhanh hơn nhiều.
Tránh móc trong constructor
Một mẹo tôi có thể cung cấp cho bạn là tránh đặt móc trong các hàm tạo. Đó là một trong những điều sẽ làm cho mã của bạn có thể kiểm tra được một cách cô lập.
Hãy xem mã kiểm tra trong OP:
class CustomPostTypes extends WP_UnitTestCase {
function test_custom_post_type_creation() {
$this->assertTrue( post_type_exists( 'foo' ) );
}
}
Và hãy giả sử thử nghiệm này thất bại . Là Ai là thủ phạm ?
- cái móc không được thêm vào hay không đúng cách?
- phương pháp đăng ký loại bài đăng hoàn toàn không được gọi hoặc với các đối số sai?
- Có lỗi gì trong WordPress?
Làm thế nào nó có thể được cải thiện?
Giả sử mã lớp của bạn là:
class RegisterCustomPostType {
function init() {
add_action( 'init', array( $this, 'register_post_type' ) );
}
public function register_post_type() {
register_post_type( 'foo' );
}
}
(Lưu ý: Tôi sẽ đề cập đến phiên bản này của lớp cho phần còn lại của câu trả lời)
Cách tôi viết lớp này cho phép bạn tạo các thể hiện của lớp mà không cần gọi add_action
.
Trong lớp trên có 2 điều cần kiểm tra:
- phương thức
init
thực sự gọi add_action
truyền cho nó các đối số thích hợp
- phương thức
register_post_type
thực sự gọi register_post_type
hàm
Tôi không nói rằng bạn phải kiểm tra xem loại bài đăng có tồn tại hay không: nếu bạn thêm hành động phù hợp và nếu bạn gọi register_post_type
, loại bài đăng tùy chỉnh phải tồn tại: nếu nó không tồn tại thì đó là vấn đề của WordPress.
Hãy nhớ rằng: khi bạn kiểm tra plugin của mình, bạn phải kiểm tra mã của mình chứ không phải mã WordPress. Trong các thử nghiệm của bạn, bạn phải cho rằng WordPress (giống như bất kỳ thư viện bên ngoài nào khác mà bạn sử dụng) hoạt động tốt. Đó là ý nghĩa của bài kiểm tra đơn vị .
Nhưng ... trong thực tế?
Nếu WordPress không được tải, nếu bạn cố gắng gọi các phương thức lớp ở trên, bạn sẽ gặp một lỗi nghiêm trọng, vì vậy bạn cần phải giả định các chức năng.
Phương pháp "thủ công"
Chắc chắn bạn có thể viết thư viện giả của mình hoặc "thủ công" giả mọi phương thức. Điều đó là có thể. Tôi sẽ cho bạn biết làm thế nào để làm điều đó, nhưng sau đó tôi sẽ chỉ cho bạn một phương pháp dễ dàng hơn.
Nếu WordPress không được tải trong khi các bài kiểm tra đang chạy, điều đó có nghĩa là bạn có thể xác định lại các chức năng của nó, ví dụ add_action
hoặc register_post_type
.
Giả sử bạn có một tệp, được tải từ tệp bootstrap của bạn, nơi bạn có:
function add_action() {
global $counter;
if ( ! isset($counter['add_action']) ) {
$counter['add_action'] = array();
}
$counter['add_action'][] = func_get_args();
}
function register_post_type() {
global $counter;
if ( ! isset($counter['register_post_type']) ) {
$counter['register_post_type'] = array();
}
$counter['register_post_type'][] = func_get_args();
}
Tôi đã viết lại các hàm để chỉ cần thêm một phần tử vào một mảng toàn cục mỗi khi chúng được gọi.
Bây giờ bạn nên tạo (nếu bạn chưa có) lớp mở rộng trường hợp kiểm tra cơ sở của riêng bạn PHPUnit_Framework_TestCase
: cho phép bạn dễ dàng định cấu hình các bài kiểm tra của mình.
Nó có thể là một cái gì đó như:
class Custom_TestCase extends \PHPUnit_Framework_TestCase {
public function setUp() {
$GLOBALS['counter'] = array();
}
}
Theo cách này, trước mỗi bài kiểm tra, bộ đếm toàn cầu được đặt lại.
Và bây giờ mã kiểm tra của bạn (tôi tham khảo lớp viết lại mà tôi đã đăng ở trên):
class CustomPostTypes extends Custom_TestCase {
function test_init() {
global $counter;
$r = new RegisterCustomPostType;
$r->init();
$this->assertSame(
$counter['add_action'][0],
array( 'init', array( $r, 'register_post_type' ) )
);
}
function test_register_post_type() {
global $counter;
$r = new RegisterCustomPostType;
$r->register_post_type();
$this->assertSame( $counter['register_post_type'][0], array( 'foo' ) );
}
}
Bạn cần lưu ý:
- Tôi đã có thể gọi hai phương thức riêng biệt và WordPress hoàn toàn không được tải. Bằng cách này nếu một thử nghiệm thất bại, tôi biết chính xác thủ phạm là ai.
- Như tôi đã nói, ở đây tôi kiểm tra rằng các lớp gọi các hàm WP với các đối số dự kiến. Không cần phải kiểm tra nếu CPT thực sự tồn tại. Nếu bạn đang kiểm tra sự tồn tại của CPT, thì bạn đang kiểm tra hành vi WordPress chứ không phải hành vi plugin của bạn ...
Hay đấy .. nhưng đó là Pita!
Có, nếu bạn phải tự chế nhạo tất cả các chức năng của WordPress, điều đó thực sự gây khó khăn. Một số lời khuyên chung tôi có thể đưa ra là sử dụng càng ít chức năng WP càng tốt: bạn không phải viết lại WordPress, nhưng các chức năng WP trừu tượng bạn sử dụng trong các lớp tùy chỉnh, để chúng có thể bị chế giễu và dễ dàng kiểm tra.
Ví dụ, liên quan đến ví dụ ở trên, bạn có thể viết một lớp đăng ký các loại bài đăng, gọi register_post_type
'init' với các đối số đã cho. Với sự trừu tượng này, bạn vẫn cần kiểm tra lớp đó, nhưng ở những nơi khác trong mã của bạn đăng ký loại bài đăng, bạn có thể sử dụng lớp đó, chế nhạo nó trong các bài kiểm tra (vì vậy giả sử nó hoạt động).
Điều tuyệt vời là, nếu bạn viết một lớp tóm tắt đăng ký CPT, bạn có thể tạo một kho lưu trữ riêng cho nó và nhờ các công cụ hiện đại như Composer nhúng nó vào tất cả các dự án mà bạn cần: kiểm tra một lần, sử dụng ở mọi nơi . Và nếu bạn từng tìm thấy một lỗi trong đó, bạn có thể sửa nó ở một nơi và đơn giản là composer update
tất cả các dự án nơi nó được sử dụng cũng được sửa.
Lần thứ hai: viết mã có thể kiểm tra được trong sự cô lập có nghĩa là viết mã tốt hơn.
Nhưng sớm hay muộn tôi cũng cần sử dụng các chức năng WP ở đâu đó ...
Tất nhiên. Bạn không bao giờ nên hành động song song với cốt lõi, nó không có ý nghĩa. Bạn có thể viết các lớp bao bọc các hàm WP, nhưng các lớp đó cũng cần phải được kiểm tra. Phương thức "thủ công" được mô tả ở trên có thể được sử dụng cho các tác vụ rất đơn giản, nhưng khi một lớp chứa nhiều hàm WP thì đó có thể là một nỗi đau.
May mắn thay, ở đó có những người tốt viết những điều tốt. 10up , một trong những cơ quan WP lớn nhất, duy trì một thư viện rất tuyệt vời cho những người muốn thử nghiệm plugin đúng cách. Đó là WP_Mock
.
Nó cho phép bạn giả định các chức năng WP một hook . Giả sử bạn đã tải trong các bài kiểm tra của mình (xem repo readme), bài kiểm tra tương tự tôi đã viết ở trên trở thành:
class CustomPostTypes extends Custom_TestCase {
function test_init() {
$r = new RegisterCustomPostType;
// tests that the action was added with given arguments
\WP_Mock::expectActionAdded( 'init', array( $r, 'register_post_type' ) );
$r->init();
}
function test_register_post_type() {
// tests that the function was called with given arguments and run once
\WP_Mock::wpFunction( 'register_post_type', array(
'times' => 1,
'args' => array( 'foo' ),
) );
$r = new RegisterCustomPostType;
$r->register_post_type();
}
}
Đơn giản phải không? Câu trả lời này không phải là hướng dẫn WP_Mock
, vì vậy hãy đọc repo readme để biết thêm thông tin, nhưng ví dụ trên nên khá rõ ràng, tôi nghĩ vậy.
Hơn nữa, bạn không cần phải viết bất kỳ bản chế add_action
hoặc register_post_type
tự chế giễu nào , hoặc duy trì bất kỳ biến toàn cục nào.
Còn lớp WP?
WP cũng có một số lớp và nếu WordPress không được tải khi bạn chạy thử nghiệm, bạn cần phải chế nhạo chúng.
Điều đó dễ dàng hơn nhiều so với các chức năng giả, PHPUnit có một hệ thống nhúng để giả lập các đối tượng, nhưng ở đây tôi muốn đề xuất Mockery cho bạn. Đây là một thư viện rất mạnh mẽ và rất dễ sử dụng. Hơn nữa, đó là một sự phụ thuộc của WP_Mock
, vì vậy nếu bạn có nó, bạn cũng có Mockery.
Nhưng còn cái gì WP_UnitTestCase
?
Bộ kiểm tra WordPress đã được tạo để kiểm tra lõi WordPress và nếu bạn muốn đóng góp vào lõi thì đó là mấu chốt, nhưng sử dụng nó cho các plugin chỉ khiến bạn kiểm tra không bị cô lập.
Hãy để mắt đến thế giới WP: có rất nhiều khung công tác PHP và CMS hiện đại và không ai trong số họ đề xuất thử nghiệm plugin / mô-đun / tiện ích mở rộng (hoặc bất cứ thứ gì chúng được gọi) bằng cách sử dụng mã khung.
Nếu bạn bỏ lỡ các nhà máy, một tính năng hữu ích của bộ phần mềm, bạn phải biết rằng có những điều tuyệt vời ở đó.
Gotchas và nhược điểm
Có một trường hợp khi quy trình làm việc tôi đề nghị ở đây thiếu: kiểm tra cơ sở dữ liệu tùy chỉnh .
Trong thực tế, nếu bạn sử dụng các bảng và hàm WordPress tiêu chuẩn để viết ở đó (ở các $wpdb
phương thức cấp thấp nhất ), bạn không bao giờ cần phải thực sự ghi dữ liệu hoặc kiểm tra nếu dữ liệu thực sự có trong cơ sở dữ liệu, chỉ cần chắc chắn rằng các phương thức thích hợp được gọi với các đối số phù hợp.
Tuy nhiên, bạn có thể viết các plugin với các bảng và hàm tùy chỉnh xây dựng các truy vấn để viết ở đó và kiểm tra xem các truy vấn đó có hoạt động không, đó là trách nhiệm của bạn.
Trong những trường hợp đó, bộ kiểm tra WordPress có thể giúp bạn rất nhiều và tải WordPress có thể cần thiết trong một số trường hợp để chạy các chức năng như dbDelta
.
(Không cần phải nói sử dụng một db khác cho các thử nghiệm, phải không?)
May mắn thay, PHPUnit cho phép bạn tổ chức các bài kiểm tra của mình trong các "bộ" có thể chạy riêng, vì vậy bạn có thể viết một bộ cho các bài kiểm tra cơ sở dữ liệu tùy chỉnh nơi bạn tải môi trường WordPress (hoặc một phần của nó) để lại tất cả các bài kiểm tra còn lại của bạn không có WordPress .
Chỉ chắc chắn viết các lớp trừu tượng càng nhiều hoạt động cơ sở dữ liệu càng tốt, theo cách mà tất cả các lớp plugin khác sử dụng chúng, để sử dụng giả, bạn có thể kiểm tra chính xác phần lớn các lớp mà không phải xử lý cơ sở dữ liệu.
Lần thứ ba, viết mã dễ dàng kiểm tra trong sự cô lập có nghĩa là viết mã tốt hơn.
phpunit
, bạn có thể thấy các bài kiểm tra thất bại hoặc vượt qua? Bạn đã cài đặtbin/install-wp-tests.sh
chưa?