Я слишком много издеваюсь или правильно выполняю TDD и модульное тестирование?

#unit-testing #laravel #mocking #tdd

#модульное тестирование #laravel #издевательство #tdd

Вопрос:

Я разрабатываю веб-приложение с помощью Laravel, и это веб-приложение должно подключаться к внутренней службе. Я решил использовать TDD в этом проекте, и теперь я сталкиваюсь с проблемой слишком большого количества зависимостей, чтобы издеваться.

В RegisterController мы разрешаем пользователю входить в систему и обновлять тип своей учетной записи, см. Код ниже:

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

    if (!empty($data['email']) amp;amp; !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|MockeryMockInterface */
    $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?

Пожалуйста, предложите мне правильный путь.

Ответ №1:

Правильно ли я выполняю модульное тестирование и TDD?

Делать TDD неправильно сложно. TDD — это процесс. Вы пишете тест, затем вы пишете код для выполнения требований теста. Просто.

С другой стороны, сделать неправильный дизайн программного обеспечения очень просто. Придерживаться принципов SOLID, поддерживать слабосвязанный код, избегать распространенных антишаблонов и предвидеть предстоящие изменения (программное обеспечение и требования постоянно меняются) очень, очень сложно. Это требует постоянного, нетривиального и неочевидного принятия решений.

Модульные тесты, будучи первыми потребителями вашего кода, будут первыми, кто выделит эти проблемы дизайна. А именно, они будут сложными, сложными для написания и хрупкими. Все эти симптомы приводят к одной причине — неправильным проектным решениям.

Как можно улучшить ваш код, чтобы смягчить эти проблемы? Присмотритесь к:

  • loginForUpgradeAccount — вход в систему зависит от обновления учетной записи? Или это более общий процесс? Могут ли ваши пользователи входить в систему и не обновлять учетную запись? Это может быть первая точка извлечения (т. Е. upgradeAccount(login) или applicationGateway->logIn(user) )
  • $this->buildErrorJsonResponse — действительно ли обновление учетной записи отвечает за генерацию ответа json? Это лучше всего вписалось бы в какую-то параметризованную фабрику ( errorResponseFactory->createForAccountUpgrade(profileData) )

Именно такие решения вам придется принимать. Модульный тест поможет вам выявить неправильные решения на ранней стадии.