【问题标题】:Am I mock too much or doing TDD and unit testing the right way?我是在模拟太多还是以正确的方式进行 TDD 和单元测试?
【发布时间】:2014-06-27 08:23:01
【问题描述】:

我正在使用 Laravel 开发 Web 应用程序,这个 Web 应用程序必须连接到内部服务。我选择在这个项目中进行 TDD,现在我面临着太多依赖项无法模拟的问题。

RegisterController,我们允许用户登录并升级他们的帐户类型,见下面的代码:

public function loginForUpgradeAccount() {
    $data = Input::only('email', 'hashedPassword');

    if (!empty($data['email']) && !empty($data['hashedPassword'])) {
        $email = $data['email'];
        $hashedPassword = $data['hashedPassword'];

        $login = $this->createLoginServiceConnector();
        $token = $login->withEmailAndHashedPassword($email, $hashedPassword);
        if ($token) {
            $profile = $this->createProfileServiceConnector($token);
            if ($profile->getData()['secret_code'] != Session::get('ThirdPartyData')['secret_code']) {
                return $this->buildErrorJsonResponse('UPGD-002', 'SecretCode not match with Third party', 400);
            }
            else {
                if ($profile->getData()['profile_type'] == 'consumer')
                    return $this->buildErrorJsonResponse('UPGD-003', 'Profile type is consumer', 400);
                else //has to call internal upgrade api
            }
        }
    }
    return $this->buildErrorJsonResponse('UPGD-001', 'Wrong email or password', 400);
}

public function createLoginServiceConnector() {
    return new Login(ApiInvokerFactory::createApiJsonInvoker());
}

public function createProfileServiceConnector($token) {
    return new Profile(ApiInvokerFactory::createApiJsonInvoker(), $token);
}

测试代码示例如下:

public function test_loginForUpgrade_must_return_error_code_UPGD_002_when_secret_code_not_match_with_third_party_data() {
    $data = array('email' => 'correct@example.com', 'hashedPassword' => '123333');
    $profileData = array(
        'secret_code' => 'abcdefg',
    );
    Session::put('ThirdPartyData', array('secret_code' => 'hijklmnop'));
    Input::shouldReceive('only')
        ->with('email', 'hashedPassword')
        ->andReturn($data);

    $login = Mockery::mock('Login');

    /** @var $login RegisterController|\Mockery\MockInterface */
    $ctrl = Mockery::mock('RegisterController');
    $ctrl->shouldDeferMissing();

    /** @var Profile||MockInterface $profile */
    $profile = Mockery::mock('Profile');

    $ctrl->shouldReceive('createLoginServiceConnector')
        ->andReturn($login);
    $login->shouldReceive('withEmailAndHashedPassword')
        ->with($data['email'], $data['hashedPassword'])
        ->andReturn('correcttokenlogin');
    $ctrl->shouldReceive('createProfileServiceConnector')
        ->with('correct_tokenlogin')
        ->andReturn($profile);
    $profile->shouldReceive('getData')
        ->andReturn($profileData);

    $result = $ctrl->loginForUpgrade();
    $response = json_decode($result->getContent(), true);
    $this->assertEquals('UPGD-002', $response['errorCode']);
}

这只是一个示例测试用例,对于一个控制器操作中的其他场景,我有非常相同的代码,例如错误的电子邮件或密码,配置文件类型是消费者等等。

我认为很难维护像这样凌乱的测试代码,如果有一天我们的升级流程发生变化或需要更多条件来升级,这会破坏我的所有测试,我必须删除并重新编写所有代码。

我是否正确地进行了单元测试和 TDD?

请给我正确的建议。

【问题讨论】:

    标签: unit-testing laravel mocking tdd


    【解决方案1】:

    我是否正确地进行了单元测试和 TDD?

    做错 TDD 很难。 TDD 是一个过程。您编写测试,然后编写代码来满足测试的要求。简单的。

    另一方面,错误的软件设计很容易。坚持SOLID 原则,保持代码松散耦合,避免常见的反模式并预测即将发生的变化(软件和需求一直在变化)非常,非常难。它需要持续的、重要的和不明显的决策。

    作为代码的第一个消费者,单元测试将首先突出这些设计问题。也就是说,它们将是复杂的、难以编写和脆弱的。所有这些症状都会导致单一原因——错误的设计决策。

    如何改进您的代码以缓解这些问题?仔细看看:

    • loginForUpgradeAccount -- 登录是为了升级帐号吗?还是更一般的过程?您的用户可以登录并且不进行帐户升级吗?这可能是第一个提取点(即upgradeAccount(login)applicationGateway->logIn(user)
    • $this->buildErrorJsonResponse -- 升级账户真的负责生成 json 响应吗?这将最适合某种参数化工厂 (errorResponseFactory->createForAccountUpgrade(profileData))

    这些是您必须做出的决定。单元测试将帮助您及早发现错误的决定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-12
      • 2020-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      相关资源
      最近更新 更多