Опыт разработки и тестирования restful json сервиса

Post on 25-Dec-2014

989 Views

Category:

Technology

5 Downloads

Preview:

Click to see full reader

DESCRIPTION

Кое-что о процессах и технологиях, которые используются при разработке системы, основой которой является RESTful JSON API.

TRANSCRIPT

Опыт разработки и тестирования RESTful JSON сервиса

Требования к системе

● Удобство для реселлеров● Возможность брендирования● Гибкость и возможность расширения за

счет подключаемых модулей

Реализация

FRONTEND

BACKEND

API

Customer

Login

Domain

VPS

...

RESTful API

● Ресурсы (объекты)● URI – идентификатор ресурса● HTTP методы (POST, GET, PUT, PATCH,

DELETE) ● Формат передачи данных на выбор (HTML,

XML, JSON, …)● Метаданные

REST + Dancer

post '/email/:domain' => sub { … };

get '/email/:domain' => sub { … };

get '/email/:domain/:mailbox' => sub { … };

put '/email/:domain/:mailbox' => sub { … };

patch '/email/:domain/:mailbox' => sub { … };

del '/email/:domain/:mailbox' => sub { … };

Dancer

dancer/ +-- config.yml +-- dancer.pl +-- customer/ | +-- lib/ | | +-- customer.pm | +-- t/ +-- login/ | +-- lib/ | | +-- login.pm | +-- t/

...

...

+-- domain/ | +-- lib/ | | +-- domain.pm | +-- t/ +-- cpanel/ | +-- lib/ | | +-- cpanel.pm | +-- t/ +-- vps/ +-- lib/ | +-- vps.pm +-- t/

Routes

get '/domain' => sub : Auth(

Admin Marketing Reseller

) {

Chimera::API::Domain->get(params());

};

Routes - Authentication

use base qw(Dancer::Chimera::App);

use Dancer qw(:syntax);

get '/domain' => sub : Auth(

Admin Marketing Reseller

) {

Chimera::API::Domain->get(params());

};

Dancer::Plugin::Auth::Extensible

Routes - Controllers

get '/domain' => sub : Auth(

Admin Marketing Reseller

) {

Chimera::API::Domain->get(params());

};

ControllersChimera/API/ +-- Basket.pm +-- Basket/ | +-- Item.pm +-- Cpanel.pm +-- Cpanel/ | +-- Addondomain.pm | +-- SSL.pm +-- Customer.pm +-- Customer/ | +-- Service/ | +-- Service.pm | +-- User/ | +-- User.pm +-- Domain.pm ... +-- Utils.pm

Server-side programsuse Dancer::Chimera qw(catch_output);

my %domain_data = catch_output('Chimera::API::Domain','create',%param

);return $domain_data{output} if $domain_data{error};

# Process output…;

Тестирование

<sam> Doing the tests afterwards is like putting a condom on after you've come.

Test-driven development

● Добавить тест● Запустить тесты: убедиться, что новые тесты

не проходят● Написать код● Запустить тесты: убедиться, что все тесты

проходят● Рефакторинг● Повторить

Тестирование API

use Test::ChimeraAPI;

Test::ChimeraAPI::run_tests(

{

title => 'Create: normal mailbox',

call => [

POST => '/email' => \%mailbox_details,

],

expect => {

http_code => HTTP_CREATED,

data => { success => 1 },

},

},

);

Тестирование API

t/ (master) $ prove -v api.t1..78ok 1 - Create: normal mailbox: HTTP codeok 2 - Create: normal mailbox data is hashref as expectedok 3 - Create: normal mailbox{success} data: scalar as expectedok 4 - Create: normal mailbox{success} data: is '1'ok 5 - Create: normal mailbox data all matches...

Тестирование

1.Написать тест и код

2.Запустить тесты

3.Увидеть кучу непонятных ошибок...

4.Понять, что сервер не перезапустился...

5.Исправить ошибки

6.Вернуться к пункту 2

Тестирование

1.Написать тест и код

2.Запустить тесты

3.Увидеть кучу непонятных ошибок...

4.Понять, что сервер не перезапустился...

5.Исправить ошибки

6.Вернуться к пункту 2

Dancer::Test

● Test::ChimeraAPI::request()– request_remote() - if $ENV{CHIMERA_SERVER}

● LWP::UserAgent::request()

– request_internal()● Dancer::Test::dancer_response()

Dancer::Test - плюсы

● Не нужно запускать сервер:– Держать открытую вкладку

– Ждать рестарта

– Получать ошибки соединения

– Не отвлекаешься от процесса кодирования

● Загружаются только нужные приложения

Dancer::Test - минусы

● Условия менее близки к реальным● Загрузка приложений при каждом запуске

теста → общее время тестирования выше

Test::Class

● Фреймворк для представления тестов в виде классов

● Удобно для тестирования ОО-кода● При работе с большим количеством тестов

Curtis (Ovid) Poe

http://www.modernperlbooks.com/mt/2009/03/organizing-test-suites-with-testclass.html

http://www.slideshare.net/Ovid/testing-with-testclass

Test::Class - плюсы

● Однократная загрузка Dancer-приложений– Может пригодиться для любых “тяжёлых”

модулей

● Много других “плюшек”– Фазы подготовки / очистки

– Наследование

– Тестирование отдельных классов через prove

– Тестирование отдельных методов

– и т.д.

Test::Class - минусы

● Требует изучения (в том числе и исходников)

● Нужна реорганизация тестов

Test::Class - тесты

t/

+-- class.t

+-- lib/

+-- Email/

+-- Test/

+-- Create.pm

+-- Delete.pm

+-- Get.pm

+-- Patch.pm

+-- Put.pm

+-- Rename.pm

+-- API.pm

class.t

#!/usr/bin/env perl

use lib::abs '../../../lib';

use our::way;

use Test::Class::Load lib::abs::path('lib');

Удалённые системы

Тесты

API-сервер

domains cpanel vps

Сторонние APIRegistrar

API cPanel API VPS API

Удалённые системы

● Взаимодействие по сети– Медленно

– Необходимо подключение к Интернет

– Поддержка тестовых серверов

● Тормоза самих систем– Создание VPS занимает ~1 мин

● Конфликты при одновременном запуске тестов

Mock-тестирование

● Пишем тесты и код● Доводим до рабочего состояния с

использованием реальной удалённой системы

● Записываем ответы сервера● Подменяем записанными ответами

реальные

Mock-тестирование - протоколы

● HTTP (LWP::UserAgent) – большая часть систем

● Другие

Test::LWP::Recorder

● Запись ответов сервера в файлы $request_md5sum

● Подстановка записанных ответов

Test::LWP::Recorder

GET http://foreign.api/something/:id - $resp1

PATCH http://foreign.api/something/:id - $resp2

GET http://foreign.api/something/:id - $resp1 (а должен быть $resp3)

Test::LWP::UserAgent

● Inspired by Test::Mock::LWP::Dispatch by Yury Zavarin

● Активно (более-менее) разрабатывается● Расширяет возможности LWP::UserAgent● Содержит интересные и полезные фичи

http://www.perladvent.org/2012/2012-12-12.html

Test::LWP::UserAgent – register_psgi

$useragent->register_psgi($hostname, $app);

Используется любой PSGI-совместимый фреймворк.

Например, Dancer :)

Но...

Тесты

API-сервер

domains cpanel vps

Сторонние APIRegistrar

API cPanel API VPS API

Процесс

Но...

Тесты

API-сервер

Процесс

API-сервер

domains cpanel vps

Сторонние APIRegistrar

API cPanel API VPS API

В Dancer всё глобально!

● Конфигурация (settings в Dancer::Config)– Serializer

● Хуки (singleton Dancer::Factory::Hook->hooks)– Аутентификация

● Переменные (vars в Dancer::SharedData)– Используются внутри нашего API, не

определены в mocked API

Инжекция mock-данных

● Test::ChimeraAPI::Mocking– Подготовка mocked классов в секции startup()

– Глобальная переменная $Mock::Something::API

● Mock-данные инжектированы в сам тестовый класс

● Mock-класс проверяет наличие данных и возвращает их в порядке timestamp или отправляет запрос к серверу

● Запись в файл при необходимости

Юнит-тестирование

Юнит-тестирование – это тестирование каждого неделимого блока функциональности в изоляции – не только возвращаемых значений для различных аргументов, но также взаимодействия этих блоков с другими частями приложения путём имитации работы этих частей.

http://tinyurl.com/c4rweaw

Mocking в юнит-тестахpackage Provisioner;use Provisioner::Mapper;

my $provisioner_mapper;__PACKAGE__->reset_provisioner_mapper;

sub reset_provisioner_mapper {shift->provisioner_mapper('Provisioner::Mapper');

}sub provisioner_mapper {

if ($_[1]) { $provisioner_mapper = $_[1] };return $provisioner_mapper;

}

sub create {my ($self, $serviceplan) = @_;my $class = $self->provisioner_mapper($serviceplan->codename);return $class->new;

}

Mocking в юнит-тестахsub provision_regrade_check_sp_is_passed_to_provisioner :Tests { my $self = shift;

Provisioner->provisioner_mapper(Chimera::UnitTest::Mock::Generic->new([ { method => 'mapping', input => ['cpanel_shared_hosting'], output => 'Chimera::UnitTest::Mock::Provisioner::CheckRegradeMethodCall' }, ]));

my($order, $action) = $self->_place_order($self->basket()); my $processed = Chimera::API::Action->process($action);

Provisioner->reset_provisioner_mapper();}

Devel::Cover

● Оценка покрытия тестами● $^P ($PERLDB) == 0x104;

– Выключена оптимизация

● Не работают атрибуты :(

Devel::Coverpackage Dancer::Chimera::App;use attributes;

my %attrs;sub MODIFY_CODE_ATTRIBUTES { my ($package, $subref, @attrs) = @_; $attrs{ refaddr $subref } = \@attrs; return;}…my $subref = sub : Auth(MyRole) { … };…

http://tinyurl.com/bptkr9a - багрепорт в perl5.porters

Документация проекта

● POD● Pod::HTML5::Browser

Код:https://github.com/LoonyPandora/Pod-HTML5-Browser

Презентация:https://speakerdeck.com/loonypandora/documentation-for-fun-and-profit

Спасибо!

Илья Чесноков <chesnokov.ilya@gmail.com>

Вопросы?

top related