#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)
)
Именно такие решения вам придется принимать. Модульный тест поможет вам выявить неправильные решения на ранней стадии.