Джеймс Уиттакер - Как тестируют в Google
if (add_url_reply.has_error_code()) {
WriteHttpReplyWithErrorDetails(http_reply, add_url_reply);
} else {
// No errors. Send HTTP 200 OK response to client.
WriteHttp200Reply(http_reply);
}
}
На функцию HandleAddUrlFrontendRequest ложится большая нагрузка — так устроены многие веб-обработчики. Разработчик может разгрузить эту функцию, выделив часть ее функциональности вспомогательным функциям. Но такой рефакторинг обычно не проводят, пока сборка не станет стабильной, а написанные юнит-тесты не будут успешно проходить.
В этом месте разработчик изменяет существующую спецификацию сборки проекта addurl, включая в нее запись для библиотеки addurl_frontend. При сборке создается статическая библиотека C++ для AddUrlFrontend.
File: /depot/addurl/BUILD
# From before:
proto_library(name="addurl",
srcs=["addurl.proto"])
# New:
cc_library(name="addurl_frontend",
srcs=["addurl_frontend.cc"],
deps=[
"path/to/httpqueryparams",
"other_http_server_stuff",
":addurl", # Link against the addurl library above.
])
Разработчик снова запускает средства сборки, исправляет ошибки компиляции и компоновщика в addurl_frontend.h и addurl_frontend.cc, пока все не будет собираться и компоноваться без предупреждений или ошибок. На этой стадии пора писать юнит-тесты для AddUrlFrontend. Они пишутся в новом файле addurl_frontend_test.cc. Тест определяет имитацию для бэкенд-системы AddUrlService и использует конструктор AddUrlFrontend для внедрения этой имитации во время тестирования. При таком подходе разработчик может внедрять ожидания и ошибки в поток операций AddUrlFrontend без изменения кода AddUrlFrontend.
File: depot/addurl/addurl_frontend_test.cc
#include "addurl/addurl.pb.h"
#include "addurl/addurl_frontend.h"
// See http://code.google.com/p/googletest/
#include "path/to/googletest.h"
// Defines a fake AddUrlService, which will be injected by
// the AddUrlFrontendTest test fixture into AddUrlFrontend
// instances under test.
class FakeAddUrlService : public AddUrlService {
public:
FakeAddUrlService()
: has_request_expectations_(false),
error_code_(0) {
}
// Allows tests to set expectations on requests.
void set_expected_url(const string& url) {
expected_url_ = url;
has_request_expectations_ = true;
}
void set_expected_comment(const string& comment) {
expected_comment_ = comment;
has_request_expectations_ = true;
}
// Allows for injection of errors by tests.
void set_error_code(int error_code) {
error_code_ = error_code;
}
void set_error_details(const string& error_details) {
error_details_ = error_details;
}
// Overrides of the AddUrlService::AddUrl method generated from
// service definition in addurl.proto by the Protocol Buffer
// compiler.
virtual void AddUrl(RPC* rpc,
const AddUrlRequest* request,
AddUrlReply* reply) {
// Enforce expectations on request (if present).
if (has_request_expectations_) {
EXPECT_EQ(expected_url_, request->url());
EXPECT_EQ(expected_comment_, request->comment());
}
// Inject errors specified in the set_* methods above if present.
if (error_code_ != 0 || !error_details_.empty()) {
reply->set_error_code(error_code_);
reply->set_error_details(error_details_);
}
}
private:
// Expected request information.
// Clients set using set_expected_* methods.
string expected_url_;
string expected_comment_;
bool has_request_expectations_;
// Injected error information.
// Clients set using set_* methods above.
int error_code_;
string error_details_;
};
// The test fixture for AddUrlFrontend. It is code shared by the
// TEST_F test definitions below. For every test using this
// fixture, the fixture will create a FakeAddUrlService, an
// AddUrlFrontend, and inject the FakeAddUrlService into that
// AddUrlFrontend. Tests will have access to both of these
// objects at runtime.
class AddurlFrontendTest : public ::testing::Test {
// Runs before every test method is executed.
virtual void SetUp() {
// Create a FakeAddUrlService for injection.
fake_add_url_service_.reset(new FakeAddUrlService);
// Create an AddUrlFrontend and inject our FakeAddUrlService
// into it.
add_url_frontend_.reset(
new AddUrlFrontend(fake_add_url_service_.get()));
}
scoped_ptr<FakeAddUrlService> fake_add_url_service_;
scoped_ptr<AddUrlFrontend> add_url_frontend_;
};
// Test that AddurlFrontendTest::SetUp works.
TEST_F(AddurlFrontendTest, FixtureTest) {
// AddurlFrontendTest::SetUp was invoked by this point.
}
// Test that AddUrlFrontend parses URLs correctly from its
// query parameters.
TEST_F(AddurlFrontendTest, ParsesUrlCorrectly) {
HTTPRequest http_request;
HTTPReply http_reply;
// Configure the request to go to the /addurl resource and
// to contain a 'url' query parameter.
http_request.set_text(
"GET /addurl?url=http://www.foo.com HTTP/1.1rnrn");
// Tell the FakeAddUrlService to expect to receive a URL
// of 'http://www.foo.com'.
fake_add_url_service_->set_expected_url("http://www.foo.com");
// Send the request to AddUrlFrontend, which should dispatch
// a request to the FakeAddUrlService.
add_url_frontend_->HandleAddUrlFrontendRequest(
&http_request, &http_reply);
// Validate the response.
EXPECT_STREQ("200 OK", http_reply.text());
}
// Test that AddUrlFrontend parses comments correctly from its
// query parameters.
TEST_F(AddurlFrontendTest, ParsesCommentCorrectly) {
HTTPRequest http_request;
HTTPReply http_reply;
// Configure the request to go to the /addurl resource and
// to contain a 'url' query parameter and to also contain
// a 'comment' query parameter that contains the
// url-encoded query string 'Test comment'.
http_request.set_text("GET /addurl?url=http://www.foo.com"
"&comment=Test+comment HTTP/1.1rnrn");
// Tell the FakeAddUrlService to expect to receive a URL
// of 'http://www.foo.com' again.
fake_add_url_service_->set_expected_url("http://www.foo.com");
// Tell the FakeAddUrlService to also expect to receive a
// comment of 'Test comment' this time.
fake_add_url_service_->set_expected_comment("Test comment");
Разработчик напишет еще много похожих тестов, но этот пример хорошо демонстрирует общую схему определения имитации, ее внедрения в тестируемую систему. Он объясняет, как использовать имитацию в тестах для внедрения ошибок и логики проверки в потоке операций тестируемой системы. Один из отсутствующих здесь важных тестов имитирует сетевой тайм-аут между AddUrlFrontend и бэкенд-системой FakeAddUrlService. Такой тест поможет, если наш разработчик забыл проверить и обработать ситуацию с возникновением тайм-аута.
Знатоки гибкой методологии тестирования укажут, что все функции FakeAddUrlService достаточно просты и вместо имитации (fake) можно было бы использовать подставной объект (mock). И они будут правы. Мы реализовали эти функции в виде имитации исключительно для ознакомления с процессом.
Теперь разработчик хочет выполнить написанные тесты. Для этого он должен обновить свои определения сборки и включить новое тестовое правило, определяющее бинарник теста addurl_frontend_test.
File: depot/addurl/BUILD
# From before:
proto_library(name="addurl",
srcs=["addurl.proto"])
# Also from before:
cc_library(name="addurl_frontend",
srcs=["addurl_frontend.cc"],
deps=[
"path/to/httpqueryparams",
"other_http_server_stuff",
":addurl", # Depends on the proto_library above.
])
# New:
cc_test(name="addurl_frontend_test",
size="small", # See section on Test Sizes.
srcs=["addurl_frontend_test.cc"],
deps=[
":addurl_frontend", # Depends on library above.
"path/to/googletest_main"])
И снова разработчик использует свои инструменты сборки для компилирования и запуска бинарного файла addurl_frontend_test, исправляет все обнаруженные ошибки компилятора и компоновщика. Кроме того, он исправляет тесты, тестовые фикстуры, имитации и саму AddUrlFrontend по всем падениям тестов. Этот процесс начинается сразу же после определения FixtureTest и повторяется при следующих добавлениях тестовых сценариев. Когда все тесты готовы и успешно проходят, разработчик создает список изменений, содержащий все файлы, а заодно исправляет все мелкие проблемы, выявленные в ходе предварительных проверок. После этого он отправляет список изменений на рецензирование и переходит к следующей задаче (скорее всего, начинает писать реальный бэкенд AddUrlService), одновременно ожидая обратной связи от рецензента.
$ create_cl BUILD
addurl.proto
addurl_frontend.h
Откройте для себя мир чтения на siteknig.com - месте, где каждая книга оживает прямо в браузере. Здесь вас уже ждёт произведение Джеймс Уиттакер - Как тестируют в Google, относящееся к жанру Программирование. Никаких регистраций, никаких преград - только вы и история, доступная в полном формате. Наш литературный портал создан для тех, кто любит комфорт: хотите читать с телефона - пожалуйста; предпочитаете ноутбук - идеально! Все книги открываются моментально и представлены полностью, без сокращений и скрытых страниц. Каталог жанров поможет вам быстро найти что-то по настроению: увлекательный роман, динамичное фэнтези, глубокую классику или лёгкое чтение перед сном. Мы ежедневно расширяем библиотеку, добавляя новые произведения, чтобы вам всегда было что открыть "на потом". Сегодня на siteknig.com доступно более 200000 книг - и каждая готова стать вашей новой любимой. Просто выбирайте, открывайте и наслаждайтесь чтением там, где вам удобно.

