3 回答
TA贡献1807条经验 获得超9个赞
尽管“只编写功能测试”可能看起来像是一种逃避(“它们不是单元测试!”),但如果您不想陷入特定于框架的知识的困境,那么这是一个合理的建议。
您会看到,这是使用外观、全局变量或静态方法带来的问题之一。为了使事情正常工作,各种各样的事情都会在您的代码(以及您的测试代码)之外发生。
问题
要了解发生了什么,您首先需要了解 Laravel 如何利用容器和工厂将事物粘合在一起。
接下来,发生的事情是:
你的代码(在某个地方
HomeController::home()
的调用中view()
。view()
调用以获取创建视图1app()
的工厂app()
来电Container::make
Container::make
呼叫Container::resolve
1Container::resolve
决定需要建造工厂并呼吁Container::build
这样做最后
Container::build
(使用 PHP 的ReflectionClass发现\Illuminate\Contracts\View\Factory
无法实例化(因为它是一个接口)并触发您看到的错误。
或者,如果您更像是一个视觉思考者:
触发错误的原因是框架期望配置容器,以便抽象类(例如接口)已知具体类。
解决方案
现在我们知道发生了什么,并且我们想创建一个单元测试,我们能做什么?
一种解决方案可能似乎不使用view
. 只需自己注入 View 类即可!但如果您尝试这样做,您很快就会发现自己走上了一条基本上在用户空间中重新创建大量框架代码的道路。所以这不是一个好主意。
更好的解决方案是模拟view()
(现在它实际上是一个单元!)。但这仍然需要仅在测试代码中重新创建框架代码。还是不太好。[3]
最简单的事情就是简单地配置容器并告诉它使用哪个类。此时,您甚至可以模拟 View 类!
现在,纯粹主义者可能会抱怨这还不够“单元”,因为您的测试仍然会在被测代码之外调用“真实”代码,但我不同意......
你正在使用框架,所以使用框架!如果您的代码使用框架提供的粘合,则测试反映此行为是有意义的。只要你不调用非粘合代码,就没有问题![4]
最后,为了让您了解如何做到这一点,举一个例子!
这个例子
假设您有一个看起来有点像这样的控制器:
namespace App\Http\Controllers;
class HomeController extends \Illuminate\Routing\Controller
{
public function home()
{
/* ... */
return view('my.view');
}
}
那么你的测试[5]可能看起来像这样:
namespace Tests\Unit\app\Http\Controllers;
use App\Http\Controllers\HomeController;
use Illuminate\Contracts\View\Factory;
class HomeControllerTest extends \PHPUnit\Framework\TestCase
{
public function testHome()
{
/*/ Arange /*/
$mockFactory = $this->createMock(Factory::class);
app()->instance(Factory::class, $mockFactory);
/*/ Assert /*/
$mockFactory->expects(self::once())
->method('make')
->with('my.view')
;
/*/ Act /*/
(new HomeController())->home();
}
}
一个更复杂的示例是还创建一个模拟视图并由模拟工厂返回它,但我将把它作为练习留给读者。
脚注
app()
要求提供接口Illuminate\Contracts\View\Factory
,但没有传递具体的类名除了调用另一个函数之外什么也不做的原因
Container::make
是方法名称make
是由 PSR-11 定义的,并且 Laravel 容器是 PSR 兼容的。此外,Laravel 提供的功能测试逻辑已经为您完成了所有这些...
只是不要忘记用
@uses
所需的粘合来注释测试,以避免在 PHPUnit 设置为有关“风险”测试的严格模式时发出警告。使用“安排、执行、断言”模式的变体
TA贡献1821条经验 获得超4个赞
TA贡献1793条经验 获得超6个赞
如果您在《未来》中发现了这一点,并且看到了 @Pothcera 的文字墙,那么您需要了解以下内容:
他这样做的唯一原因以及您在单元测试中首先看到这一点的唯一原因是因为他和您没有在测试文件中从 更改PHPUnit\Framework\TestCase
为。Tests\TestCase
当您扩展包含app()
.
我的建议是简单地扩展正确的基础测试用例并继续你的生活。
- 3 回答
- 0 关注
- 134 浏览
添加回答
举报