You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对建造者设计模式进行单元测试?附CourseListingBuilder类示例

嘿,针对你这个CourseListingBuilder建造者类的单元测试,我给你梳理几个核心方向和具体的测试用例,都是结合你给出的代码来的:

核心测试思路

建造者模式的单元测试重点在于验证每个建造方法是否正确修改了内部状态,以及最终构建出的产物(这里是yii\db\Query对象)是否符合预期。我们可以分模块来测试每个方法的行为。

1. 测试静态创建方法 create()

这个方法的职责很明确:返回一个全新的CourseListingBuilder实例,并且确保内部的query已经被正确初始化。

public function testCreateReturnsValidInstance()
{
    $builder = CourseListingBuilder::create();
    
    // 验证返回的是正确的实例类型
    $this->assertInstanceOf(CourseListingBuilder::class, $builder);
    
    // 用反射访问私有属性,确认query已初始化
    $reflection = new ReflectionClass($builder);
    $queryProperty = $reflection->getProperty('query');
    $queryProperty->setAccessible(true);
    
    $this->assertInstanceOf(yii\db\Query::class, $queryProperty->getValue($builder));
}

2. 测试 selectColumns() 方法

这个方法是你标注了@TODO的核心方法,测试要覆盖合法参数和非法参数两种场景,确保验证和格式化逻辑生效。

2.1 合法列参数场景

验证传入的字符串数组被正确同步到内部的query对象中:

public function testSelectColumnsWithValidColumns()
{
    $builder = CourseListingBuilder::create();
    $validColumns = ['id', 'course_name', 'price'];
    
    $builder->selectColumns($validColumns);
    
    // 检查query的select属性是否和传入的列一致
    $reflection = new ReflectionClass($builder);
    $queryProperty = $reflection->getProperty('query');
    $queryProperty->setAccessible(true);
    $query = $queryProperty->getValue($builder);
    
    $this->assertEquals($validColumns, $query->select);
}

2.2 非法列参数场景

假设你打算在传入空数组、非字符串元素时抛出InvalidArgumentException,测试用例如下:

public function testSelectColumnsWithInvalidColumnsThrowsException()
{
    $builder = CourseListingBuilder::create();
    
    // 测试空数组
    $this->expectException(InvalidArgumentException::class);
    $builder->selectColumns([]);
    
    // 测试包含非字符串的数组(比如数字、对象)
    $this->expectException(InvalidArgumentException::class);
    $builder->selectColumns(['id', 123, new stdClass()]);
}

3. 测试链式调用(可选)

如果你的建造者后续支持链式调用(比如每个方法返回$this),可以加一个测试验证这一点,确保建造者的使用体验符合预期:

public function testSelectColumnsSupportsMethodChaining()
{
    $builder = CourseListingBuilder::create();
    $result = $builder->selectColumns(['id', 'course_name']);
    
    // 验证返回的是同一个实例,支持链式调用
    $this->assertSame($builder, $result);
}

额外优化建议

  • 隔离依赖:如果不想依赖真实的yii\db\Query对象,可以用PHPUnit的Mock功能来模拟它,这样测试不需要连接数据库,速度更快也更纯粹:
    public function testSelectColumnsWithMockedQuery()
    {
        // 创建一个Mock的Query对象,只关注select方法
        $mockQuery = $this->getMockBuilder(yii\db\Query::class)
            ->onlyMethods(['select'])
            ->getMock();
        
        // 预期select方法会被调用一次,参数是指定的列
        $mockQuery->expects($this->once())
            ->method('select')
            ->with(['id', 'course_name']);
        
        $builder = CourseListingBuilder::create();
        // 用反射替换builder内部的query为Mock对象
        $reflection = new ReflectionClass($builder);
        $queryProperty = $reflection->getProperty('query');
        $queryProperty->setAccessible(true);
        $queryProperty->setValue($builder, $mockQuery);
        
        $builder->selectColumns(['id', 'course_name']);
    }
    
  • 测试init()方法:虽然create()会自动调用init(),但如果init()里有特殊的初始化逻辑(比如父类方法的调用),也可以单独写测试验证。

内容的提问来源于stack exchange,提问作者Dzhuneyt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:41:32