如何对Builder设计模式进行单元测试

时间:2018-05-03 07:16:39

标签: unit-testing yii2

如果我们有以下使用Builder设计模式的类:

class CourseListingBuilder extends Component
{

    /**
     * @var yii\db\Query
     */
    private $query;
    private $data = [];

    public function init()
    {
        parent::init();

        $this->query = new yii\db\Query();
    }

    /**
     * Return a new instance of the builder
     * @return CourseListingBuilder
     */
    public static function create()
    {
        return new CourseListingBuilder();
    }

    public function selectColumns(array $columns)
    {
        // @TODO validate and format $columns
        $this->query->select($columns);
        return $this;
    }

    public function applyFilters($filters = [])
    {
        // @TODO validate and parse filters
        $this->query->andWhere($filters);
        return $this;
    }

    public function build()
    {
        // Make the actual DB query
        $this->data = $this->query->all();
    }

    public function getData()
    {
        return $this->data;
    }

}

我们这样使用它:

$data = CourseListingBuilder::create()
->selectColumns(['id', 'name'])
->applyFilters(['active'=>1])
->build()
->getData();

随着时间的推移,这些类变得非常大,因为我们有多个开发人员正在开发它们。开发人员扩展了selectColumns()applyFilters(),偶尔也会延迟代码中断。我们最近开始在公司中引入单元测试,我们希望制作符合上述模式的类 - 单元可测试,最好通过小型重构。

如何对上面的构建器设计模式类进行适当的单元测试,因为它内部依赖于yii\db\Query(顺便说一句,它来自Yii2框架,但这与示例无关)。与该示例相关的是我们不想测试yii\db\Query的内部行为。它来自框架。我们知道它正在发挥作用。换句话说 - 我们希望尽可能“模拟”它,同时有效地测试Builder类中的实际方法以及它们将如何影响结果。

要指出的第二点是我们知道如何编写单元测试。这个问题一般不是关于“如何编写单元测试”,而是“如何为与第三方DAO类具有内部依赖性的构建器类编写单元测试”。

  • 我们做错了吗?
  • 此类代码不是单元可测试的,而是进入集成测试领域?

1 个答案:

答案 0 :(得分:1)

最简单的方法是用一些简单的类替换yii\db\Query并在方法调用后测试其状态。

class MockQuery extends \yii\db\Query {

    public $select;
    public $selectOption;

    public function select($columns, $option = null) {
        $this->select = $columns;
        $this->selectOption = $option;
    }

    // ...
}

class CourseListingBuilder extends \yii\base\Component {

    private $query;

    public function selectColumns(array $columns) {
        // @TODO validate and format $columns
        $this->query->select($columns);
        return $this;
    }

    // ...
}

并测试:

public function testQuery() {
    $builder = new CourseListingBuilder();

    // use reflection to access private property
    $reflection = new \ReflectionObject($builder);
    $property = $reflection->getProperty('query');
    $property->setAccessible(true);
    $property->setValue($builder, new MockQuery());

    $builder->selectColumns(['id', 'name']);

    $query = $property->getValue($builder);
    $this->assertSame(['id', 'name'], $query->select);
    $this->assertNull($query->selectOption);
}