测试覆盖特征方法执行
Testing overriden trait method execution
我有这样的情况。我有一些第 3 方特征(我不想测试)并且我有使用这个特征的特征,在某些情况下 运行s 第 3 方特征方法(在下面的例子中我总是 运行 它).
当我有这样的代码时:
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** @test */
public function it_runs_parent_method_alternative()
{
$class = Mockery::mock(B::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y2 {
function foo() {
$this->fooX();
return 'bar';
}
}
class B {
use Y2, X {
Y2::foo insteadof X;
X::foo as fooX;
}
}
它会工作正常,但我不希望代码这样组织。在上面的 class 代码中,我同时使用了这两个特征,但在我想测试的代码中,实际上特征使用了开头提到的其他特征。
但是当我有这样的代码时:
<?php
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** @test */
public function it_runs_parent_method()
{
$class = Mockery::mock(A::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->fooX();
return 'bar';
}
}
class A {
use Y;
}
我得到:
undefined property $something
所以在这种情况下,Mockery 似乎不再嘲笑 X::foo 方法。有没有办法可以用这样组织的代码编写这样的测试?
到目前为止,无法模拟更深层的别名方法。您可以使用本地方法代理别名方法调用并允许模拟受保护的方法。
检查下面的代码
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** @test */
public function it_runs_parent_method()
{
$mock = Mockery::mock(A::class)->shouldAllowMockingProtectedMethods()->makePartial();
$mock->shouldReceive('proxyTraitCall')->once();
$this->assertSame('bar', $mock->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->proxyTraitCall();
return 'bar';
}
function proxyTraitCall() {
return $this->fooX();
}
}
如果你自动加载 trait,你可以尝试 overload 使用 Mockery。
/** @test */
public function it_runs_parent_method()
{
$trait = Mockery::mock("overload:" . X::class);
$trait->shouldReceive('foo')->once();
$class = Mockery::mock(A::class)->makePartial();
$this->assertSame('bar', $class->foo());
}
Don't test implementation details.像使用一样测试它。
Class 用户只需要知道 public 接口就可以使用它,为什么测试应该有什么不同?
事实上,一种内部方法调用不同的方法是实现细节,测试这会破坏封装。如果有一天你从 trait 切换到 class 方法而不改变 class 行为,你将不得不修改测试,即使 class 从外面看起来是一样的。
来自 Dave Thomas 和 Andy Hunt 的实用单元测试
Most of the time, you should be able to test a class by exercising its
public methods. If there is significant functionality that is hidden
behind private or protected access, that might be a warning sign that
there's another class in there struggling to get out.
我有这样的情况。我有一些第 3 方特征(我不想测试)并且我有使用这个特征的特征,在某些情况下 运行s 第 3 方特征方法(在下面的例子中我总是 运行 它).
当我有这样的代码时:
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** @test */
public function it_runs_parent_method_alternative()
{
$class = Mockery::mock(B::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y2 {
function foo() {
$this->fooX();
return 'bar';
}
}
class B {
use Y2, X {
Y2::foo insteadof X;
X::foo as fooX;
}
}
它会工作正常,但我不希望代码这样组织。在上面的 class 代码中,我同时使用了这两个特征,但在我想测试的代码中,实际上特征使用了开头提到的其他特征。
但是当我有这样的代码时:
<?php
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** @test */
public function it_runs_parent_method()
{
$class = Mockery::mock(A::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->fooX();
return 'bar';
}
}
class A {
use Y;
}
我得到:
undefined property $something
所以在这种情况下,Mockery 似乎不再嘲笑 X::foo 方法。有没有办法可以用这样组织的代码编写这样的测试?
到目前为止,无法模拟更深层的别名方法。您可以使用本地方法代理别名方法调用并允许模拟受保护的方法。
检查下面的代码
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** @test */
public function it_runs_parent_method()
{
$mock = Mockery::mock(A::class)->shouldAllowMockingProtectedMethods()->makePartial();
$mock->shouldReceive('proxyTraitCall')->once();
$this->assertSame('bar', $mock->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->proxyTraitCall();
return 'bar';
}
function proxyTraitCall() {
return $this->fooX();
}
}
如果你自动加载 trait,你可以尝试 overload 使用 Mockery。
/** @test */
public function it_runs_parent_method()
{
$trait = Mockery::mock("overload:" . X::class);
$trait->shouldReceive('foo')->once();
$class = Mockery::mock(A::class)->makePartial();
$this->assertSame('bar', $class->foo());
}
Don't test implementation details.像使用一样测试它。
Class 用户只需要知道 public 接口就可以使用它,为什么测试应该有什么不同? 事实上,一种内部方法调用不同的方法是实现细节,测试这会破坏封装。如果有一天你从 trait 切换到 class 方法而不改变 class 行为,你将不得不修改测试,即使 class 从外面看起来是一样的。
来自 Dave Thomas 和 Andy Hunt 的实用单元测试
Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out.