lifterlms/lifterlms-tests

LifterLMS项目的测试框架

4.4 2024-09-09 17:12 UTC

README

LifterLMS Tests是一个帮助在LifterLMS项目中启动自动化测试的项目。

安装

  • 安装包:composer require --dev lifterlms/lifterlms-tests
  • 创建一个phpunit.xml.dist文件。参见示例
  • 添加一个tests目录:mkdir tests
  • tests目录中创建一个引导文件。参见示例
  • tests/unit-tests中添加测试类

命令

  • 安装测试套件:./vendor/bin/llms-tests install <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]
  • 拆卸测试套件:.vendor/bin/llms-tests teardown <db-name> <db-user> <db-pass> [db-host]
  • 安装插件:./vendor/bin/llms-tests plugin <slug_or_zip_giturl> [version]
  • 运行测试:./vendor/bin/phpunit
  • 环境:./vendor/bin/llms-env <command> [options]。查看./vendor/bin/llms-env --help获取完整文档。

环境

llms-env命令提供了一套简单的工具,有助于管理和使用Docker容器进行LifterLMS插件的开发和测试。

对于定制,运行llms-env config在项目根目录中创建推荐的docker-compose.yml.llmsenv文件。

docker-compose.yml会自动将根目录挂载到wp-content/plugin目录。

.llmsenv文件允许自定义WordPress用户名、localhost端口等。此文件是可选的,如果省略任何变量,将使用默认值。

配置好配置文件后,运行llms-env up来创建容器、安装和配置WordPress。

如果需要其他插件或任何其他WordPress配置,尝试创建一个composer脚本,通过wp-cli在主PHP服务容器上运行所需命令。例如,在您的composer.json中添加一个脚本

{
  "scripts": {
    "env-setup": [
      "./vendor/bin/llms-env wp plugin install lifterlms --activate"
    ]
  }
}

这可以用来激活LifterLMS核心。

llms-env提供了许多用于组合和管理Docker容器的命令。运行llms-env --help获取可用命令的完整列表以及它们的使用信息。

预定义脚本

以下脚本可以添加到您的composer.json文件中,以便轻松访问这些脚本,并确保在安装和更新包时自动设置配置。

"scripts": {
    "tests-install": [
      "vendor/bin/llms-tests teardown llms_tests root password localhost",
      "vendor/bin/llms-tests install llms_tests root password localhost",
      "vendor/bin/llms-tests plugin lifterlms"
    ],
    "tests-run": [
      "vendor/bin/phpunit"
    ]
}

实用工具

实用方法位于LLMS_Unit_Test_Util类中。

获取私有/受保护的类方法

$method = LLMS_Unit_Test_Util::get_private_method( new MyClass(), 'my_private_method' );
$method = LLMS_Unit_Test_Util::get_protected_method( new MyClass(), 'my_private_method' );

调用私有/受保护的类方法进行测试

$result = LLMS_Unit_Test_Util::call_method( new MyClass(), 'my_private_method', array( 'argument_1', 'arg_2', ... ) );
$this->assertTrue( $result );

函数

使用llms_tests_mock_current_time( $timestamp_or_date )模拟llms_current_time()的返回值

使用llms_tests_reset_current_time()重置当前时间

测试用例方法

继承自LLMS_Unit_Test_Case类的测试用例可以访问测试用例类中内置的实用函数。

断言

继承自LLMS_Unit_Test_Case类的测试用例可以访问测试用例类中内置的实用函数。

资产

断言资产(脚本或样式)是否通过WordPress依赖管理类注册或排队(或相反,未注册或未排队)。

$type是"script"或"style",$handle是用于注册/排队的脚本的手柄。

  • 断言脚本/样式已注册:$this->assertAssetIsRegistered( $type, $handle )
  • 断言脚本/样式未注册:$this->assertAssetNotRegistered( $type, $handle )
  • 断言脚本/样式已排队:$this->assertAssetIsEnqueued( $type, $handle )
  • 断言脚本/样式未被排队:$this->assertAssetNotEnqueued( $type, $handle )
输出缓冲区
  • 断言函数输出包含字符串:$this->assertOutputContains( $contains, $callable, $args_array );
  • 断言函数输出不包含字符串:$this->assertOutputNotContains( $contains, $callable, $args_array );
  • 断言函数输出为空:$this->assertOutputEmpty( $callable, $args_array );
  • 断言函数输出等于预期:$this->assertOutputEquals( $expected, $callable, $args_array );
WP_Error
  • 断言对象是 WP_Error$this->assertWPError( $wp_err );
  • 断言 WP_Error 代码等于预期:$this->assertWPErrorCodeEquals( $code, $wp_err );
  • 断言 WP_Error 消息等于预期:$this->assertWPErrorMessageEquals( $message, $wp_err );
  • 断言 WP_Error 数据等于预期:$this->assertWPErrorDataEquals( $data, $wp_err );
模拟 $_GET$_POST$_REQUEST 数据

通过 $this->mockGetRequest( array( 'var' => 'value' ) ); 添加模拟 $_GET 数据

通过 $this->mockPostRequest( array( 'var' => 'value' ) ); 添加模拟 $_POST 数据

模拟通过 wp_remote_request() 进行的 HTTP 请求。

在调用 wp_remote_request() 之前,运行 $this->mock_http_request( $url, $data, $fuzzy_match ) 以设置下一次 wp_remote_request() 的期望返回值。

当运行 wp_remote_request() 时,模拟器将检查是否为 URL 添加了模拟。如果找到,它将短路 HTTP 请求并在任何远程连接之前提前返回(返回 $data 的值)。一旦找到并返回模拟,它将从模拟器数据中删除。如果您希望模拟多个连续的 URL,您可以多次调用 mock_http_request()。匹配器始终返回 第一个 匹配项。因此,如果您希望对相同的 URL 进行多次模拟,请确保按照预期返回的顺序设置模拟。

您可以将完整的或部分 URL 作为 $url 参数指定。如果指定部分 URL,请使用 $fuzzy_match = true 来匹配 URL 部分。

public function test_mock_https_request() {

  // Mocks a WP REST post creation request.
  $this->mock_http_request( '/wp-json/wp/v2/posts',
    [ 
      'body'     => '{"id":123,"title":"Mock Title",...}',
      'response' => [
        'code' => 201,
      ],
    ], 
    true
  );

  $res = wp_remote_post( 
    rest_url( '/wp-json/wp/v2/posts' ),
    [
      'body' => [
        'title' => 'Mock Title',
      ],
    ],
  );

  $this->assertEquals( 201, wp_remote_retrieve_response_code( $res ) );
  $this->assertEquals( 123, json_decode( wp_remote_retrieve_response_body( $res ) )['id'] );

}


##### Utility Methods

+ Get the output of a function: `$output = $this->get_output( $callable, $args_array );`

## Exceptions

Included exceptions allow easy testing of methods which call `exit()` and `llms_redirect_and_exit()`.

##### LLMS_Unit_Test_Exception_Exit

Test methods which call `exit()`: Call `$this->expectException( LLMS_Unit_Test_Exception_Exit::class );` before calling the function that calls exit.

```php
public function test_example_exit() {
  $this->expectException( LLMS_Unit_Test_Exception_Exit::class );
  example_function_that_exits();
}
LLMS_Unit_Test_Exception_Redirect

测试调用 llms_redirect_and_exit() 的方法

public function test_my_redirect_and_exit() {
  $this->expectException( LLMS_Unit_Test_Exception_Redirect::class );
  $this->expectExceptionMessage( 'https://lifterlms.com [302] YES' );
  llms_redirect_and_exit( 'https://lifterlms.com' );
}

异常会导致 PHP 执行停止。要在遇到异常后运行更多测试,请添加 try/catch 块

public function test_my_redirect_and_exit() {
  $this->expectException( LLMS_Unit_Test_Exception_Redirect::class );
  $this->expectExceptionMessage( 'https://lifterlms.com [302] YES' );
  try {
    llms_redirect_and_exit( 'https://lifterlms.com' );
  } catch( LLMS_Unit_Test_Exception_Redirect $exception ) {
    // Any additional assertions can be added here.
    $this->assertTrue( ... );
    throw $exception;
  }
}

工厂

扩展 LLMS_Unit_Test_Case 类的测试用例可以访问基于 WP 单元测试工厂构建的工厂:WP_UnitTest_Factory_For_PostWP_UnitTest_Factory_For_User

课程帖子工厂

访问工厂:$this->factory->course

创建一个课程并获取课程 ID:$course_id = $this->factory->course->create();

创建一个课程并获取 LLMS_Course 对象:$course = $this->factory->course->create_and_get();

创建多个课程:$courses = $this->factory->course->create_many( 5 );

指定章节、课程、测验和问题的数量

$args = array(
  'sections' => 2, // 2 sections in the course
  'lessons' => 5, // 5 lessons per section
  'quizzes' => 1, // 1 quiz per section (will always be the last lesson in the section)
  'questions' => 5, // 5 questions per quiz
);
$course_id = $this->factory->course->create( $args );
$course = $this->factory->course->create_and_get( $args );
会员帖子工厂

访问工厂:$this->factory->membership

创建一个会员并获取会员 ID:$membership_id = $this->factory->membership->create();

创建一个会员并获取 LLMS_Membership 对象:$membership = $this->factory->membership->create_and_get();

创建多个会员:$memberships = $this->factory->membership->create_many( 5 );

订单帖子工厂

访问工厂:$this->factory->order

创建一个订单并获取订单 ID:$order_id = $this->factory->order->create();

创建一个订单并获取 LLMS_Order 对象:$order = $this->factory->order->create_and_get();

创建多个订单:$orders = $this->factory->order->create_many( 5 );

创建一个订单并为其记录交易:LLMS_Order:$order = $this->factory->order->create_and_pay();

学生用户工厂

访问工厂:$this->factory->student

创建一个学生并获取学生 ID:$student_id = $this->factory->student->create();

创建学生并获取LLMS_Student对象:$student = $this->factory->student->create_and_get();

创建多个学生:$students = $this->factory->student->create_many( 5 );

创建学生并注册课程/会员

$course_id = $this->factory->course->create();
// single student
$student_id = $this->factory->student->create_and_enroll( $coursed_id );
// multiple students
$student_ids = $this->factory->student->create_and_enroll_many( 5, $coursed_id );
讲师用户工厂

访问工厂:$this->factory->instructor

创建讲师并获取讲师ID:$instructor_id = $this->factory->instructor->create();

创建讲师并获取LLMS_Instructor对象:$instructor = $this->factory->instructor->create_and_get();

Cookie

使用LLMS_Tests_Cookies类测试通过llms_setcookie设置的cookie的方法和函数。

访问类:$this->cookies.

设置cookie
$this->cookies->set( $name, $value, ... )
检索设置的cookie(s)
$this->cookies->set( 'name', 'value', 0, ... );

// Retrieve all cookies
$cookies = $this->cookies->get_all();
var_dump( $cookies );
// array(
//   'name' => array(
//      'value'   => 'value',
//      'expires' => 0,
//      ...
//   ),
// )
//

// Retrieve a single cookie.
$cookie = $this->cookies->get( 'name' );
var_dump( $cookie );
// array(
//    'value'   => 'value',
//    'expires' => 0,
//    ...
//   ),
模拟llms_setcookie()的预期响应

模拟成功响应

$this->cookies->expect_success();
$this->assertTrue( llms_setcookie( 'name', 'val' ) );

在测试套件中,llms_setcookie()总是响应true,所以不需要调用expect_success(),除非你之前在同一测试中调用了expect_error()

模拟错误响应

$this->cookies->expect_error();
$this->assertFalse( llms_setcookie( 'name', 'val' ) );
清除设置的cookie(s)

清除所有cookie

$this->cookies->unset_all()
var_dump( $this->cookies->get_all() );
// array()

通过名称清除单个cookie

llms_setcookie( 'name', 'val' );
$this->cookies->unset( 'name' );
var_dump( $this->cookies->get( 'name' ) );
// null