ernestmarcinko/mockutils

PHPUnit 测试模拟工具

1.0.3 2024-03-28 11:52 UTC

This package is auto-updated.

Last update: 2024-09-28 13:21:03 UTC


README

tests

本包通过 MockUtils 特性为 PHPUnit 的 TestCase 类(及其子类)提供了一些实用函数

  • 全局模拟 - setGlobalMocks()unsetGlobalMocks() 方法用于设置命名空间内的全局函数模拟
  • 异常断言 - 通过 expectCatchException() - 与 expectException() 方法类似,但不会终止测试执行

安装与包含

要安装此包

composer require ernestmarcinko/mockutils --dev

在你的测试中包含

有两种方法,一种是通过特性扩展你的 TestCase 功能(推荐)

use ErnestMarcinko\MockUtils\MockUtils;
use PHPUnit\Framework\TestCase;

class MyTest extends TestCase {
	use MockUtils;
	...
}

..或者使用 MockUtilsTestCase 类代替 TestCase

use ErnestMarcinko\MockUtils\MockUtilsTestCase;

class MyTest extends MockUtilsTestCase {
	...
}

MockUtilsTestCase 只是一个扩展了 TestCase 并使用了 MockUtils 的空类。

全局模拟

特性为 setGlobalMocks()unsetGlobalMocks() 方法添加了两个实用函数,用于设置和取消全局模拟。

setGlobalMocks

protected function setGlobalMocks(array $global_mocks, ?string $namespace): void

将全局模拟设置为给定的命名空间。

参数

示例

假设你有一个服务,它使用 curl_exec 从 API 获取响应。在测试中,你想模拟它,避免实际连接到 API,而是使用预定义的响应进行测试。

namespace MyNamespace\MySubNamespace;

class MyClass {
	public function handler() {
		//....
		
		$response = curl_exec($curl); // you want to mock this
		
		//You do something with $response below
	}
}

在测试中,我们需要 curl_exec 返回 'response' 作为模拟,为此

namespace MyTestNamespace;

class TestMyClass {
	public function testHandler() {
		$this->setGlobalMocks(
			[
				'curl_exec' => 'response'
			], 
			'MyNamespace\\MySubNamespace'
		)
	
		$o = new MyClass();
		$this->assertSame( 'expected output', $o->handler() );
	}
}

也可以定义一个可调用对象而不是静态响应

namespace MyTestNamespace;

class TestMyClass {
	public function testHandler() {
		$this->setGlobalMocks(
			[
				'curl_exec' => function($curl) {
					return 'response';
				}
			], 
			'MyNamespace\\MySubNamespace'
		)
	
		$o = new MyClass();
		$this->assertSame( 'expected output', $o->handler() );
	}
}

unsetGlobalMocks

protected function unsetGlobalMocks(?array $global_mocks=null): void

取消给定全局模拟或之前定义的所有全局模拟。

参数

示例

namespace MyTestNamespace;

class TestMyClass {
	public function testHandler() {
		$this->setGlobalMocks(
			[
				'time' => fn()=>time()-3600,
				'json_decode' => array(),
				'strval' => fn($v)=>$v,
			], 
			'MyNamespace\\MySubNamespace'
		)
		$o = new MyClass();
		$this->assertSame( 'expected output 1', $o->handler() );
		
		$this->unsetGlobalMocks(array('time')); // unset just the time mock
		$this->assertSame( 'expected output 2', $o->handler() );
		
		$this->unsetGlobalMocks(); // unset all remaining mocks
		$this->assertSame( 'expected output 3', $o->handler() );
	}
}

异常断言

expectCatchException

检查是否抛出了异常 而不终止 执行。

protected function expectCatchException(callable $fn, string $throwable, ?string $message = null): void 

与 PHPUnit 核心库的 TestCase::expectException 相比,此函数不会终止测试执行。但是,必须将测试函数传递为一个闭包,例如: expectCatchException(fn()=>$o->myMethod(), ...)

参数

返回值

此函数为空,不返回任何内容

expectCatchException 如果没有抛出异常或未满足任何条件,将触发 TestCase::fail()

示例

namespace MyTestNamespace;

class TestMyClass {
	public function testHandler() {
		$this->expectCatchException(function(){
			throw new Exception('hey!');
		}, Exception::class);
		
		// Execution continues
		
		$this->expectCatchException(function(){
			throw new Exception('hey!');
		}, Exception::class, 'hey!');
		
		$o = new MyClass();
		$this->expectCatchException(fn()=>$o->handle(), Exception::class);
		
		$this->expectCatchException(
			fn()=>$o->handle(), 
			Exception::class,
			'Exception message!'
		);
	}
}