Тестирование выполнения переопределенного метода признака


У меня такая ситуация. У меня есть какая-то сторонняя черта (я не хочу тестировать), и у меня есть моя черта, которая использует эту черту и в некоторых случаях запускает сторонний метод (в приведенном ниже примере я всегда его запускаю).

Когда у меня есть такой код:

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;
    }
}

Это будет работать нормально, однако я не хочу, чтобы код был организован таким образом. В приведенном выше коде в классе я использую обе черты, но в коде я хочу проверить, на самом деле черта использует другую черту, как упоминалось в начало.

Однако, когда у меня есть такой код:

<?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;
}

Я получаю:

Неопределенное свойство $что-то

Так что, похоже, Издевательство в данном случае больше не является издевательством над методом X::foo. Есть ли способ сделать возможным написание таких тестов с помощью кода, организованного подобным образом?

Author: Marcin Nabiałek, 2018-08-04

1 answers

До сих пор невозможно имитировать более глубокие методы с псевдонимами. Вы можете вызвать прокси-метод с псевдонимами, используя локальный метод и разрешая издеваться над защищенными методами.

Проверьте код ниже

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();
    }
}

Если вы загружаете черту автоматически, вы можете попытаться перегрузить ее с помощью издевательства.

/** @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());
}

Не проверяйте детали реализации. Протестируйте его так, как вы его используете.

Пользователь класса должен знать только общедоступный интерфейс, чтобы использовать его, почему тест должен быть другим? Факт, что один внутренний вызов метода, отличный от другого, - это детали реализации, и тестирование этого нарушает инкапсуляцию. Если когда-нибудь вы переключитесь с метода признака на метод класса без изменения поведения класса, вам придется модифицировать тесты, даже если класс снаружи выглядит одинаково.

Из Прагматического модульного тестирования Дэйва Томаса и Энди Ханта

В большинстве случаев вы должны иметь возможность протестировать класс, используя его общедоступные методы. Если есть значительная функциональность это скрыто за частным или защищенным доступом, это может быть предупреждающим признаком того, что там есть еще один класс, который пытается выбраться.

 2
Author: mleko, 2018-08-14 07:34:14