我是在嘲笑太多还是在以正确的方式进行TDD和单元测试? [英] Am I mock too much or doing TDD and unit testing the right way?

查看:70
本文介绍了我是在嘲笑太多还是在以正确的方式进行TDD和单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm developing web application by using Laravel and this web app has to connect to internal service. I choose to do TDD in this project and now I'm facing with problem about too much dependencies to mock.

RegisterController中,我们允许用户登录并升级其帐户类型,请参见以下代码:

In RegisterController, we allow user to login and upgrade their account type, see code below:

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']);
}

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

This is only one example test case and I have very same code for other scenario in one controller action like wrong email or password and profile type is consumer and much more.

我认为很难像这样维护混乱的测试代码,如果某天我们的升级流程发生变化或需要更多条件来升级,这将破坏我的所有测试,因此我必须再次删除并重写所有内容.

I think it hard to maintain messy test code like this and if someday our flow for upgrade is changed or require more condition to upgrade this would broke all of my test and I have to delete and rewrite all again.

我可以正确执行单元测试和TDD吗?

Am I do unit testing and TDD correctly?

请以正确的方式建议我.

Please suggest me the right way.

推荐答案

我可以正确执行单元测试和TDD吗?

Am I do unit testing and TDD correctly?

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

Doing TDD wrong is hard. TDD is a process. You write test then you write code to fulfill test's requirements. Simple.

另一方面,错误地进行软件设计非常容易.坚持 SOLID 原则,使代码保持宽松耦合 >,避免常见的反模式并预期即将到来的更改(软件和需求始终在变化)非常,非常困难.它需要持续,非平凡和非显而易见的决策.

On the other hand, doing software design wrong is very easy. Sticking to SOLID principles, keeping your code loosely coupled, avoiding common anti-patterns and anticipating upcoming changes (software and requirements change all the time) is very, very hard. It requires constant, non-trivial and non-obvious decision making.

作为单元代码的第一使用者,单元测试将是第一个突出这些设计问题的人.即,它们将是复杂的,难以编写的并且易碎的.所有这些症状都导致单一原因-错误的设计决策.

Unit tests, being first consumers of your code will be the first to highlight those design issues. Namely, they will be complex, hard to write and fragile. All those symptoms lead to single cause -- incorrect design decisions.

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

How can your code be improved to mitigate those problems? Take a closer look at:

  • loginForUpgradeAccount-登录特定于帐户升级吗?还是更一般的过程?您的用户可以登录并且不执行升级帐户吗?这可能是第一个提取点(即upgradeAccount(login)applicationGateway->logIn(user))
  • $this->buildErrorJsonResponse-升级帐户真的负责json响应生成吗?最好将其安装到某种类型的参数化工厂(errorResponseFactory->createForAccountUpgrade(profileData))
  • loginForUpgradeAccount -- is logging in specific to account upgrade? Or is it more general process? Can your users log in and not do an account upgrade? This may be first extraction point (ie. upgradeAccount(login) or applicationGateway->logIn(user))
  • $this->buildErrorJsonResponse -- is upgrading account really responsible for json response generation? This would be best fitted into parameterized factory of some sort (errorResponseFactory->createForAccountUpgrade(profileData))

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

Those are the kind of decisions you'll have to make. Unit test will help you spotting wrong decisions early.

这篇关于我是在嘲笑太多还是在以正确的方式进行TDD和单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆