Магистр ДонНТУ Ларин Борислав Игоревич

Тестирование приложений Google App Engine

Введение

Вряд ли сейчас найдется человек, интересующийся современными компьютерными технологиями, который не слышал модного словечка «cloud», «облако». В последнее время тенденция переноса рабочих инструментов и данных из локального хранилища пользователя в облачные сервисы набирает огромные обороты. По оценкам экспертов, объем европейского рынка облачных вычислений достигнет 35 миллиардов евро к 2014 году.

Такие корпорации как Microsoft, Apple, IBM, HP вкладывают деньги в разработку облачных сервисов и инфраструктуры для своих клиентов.


Логотип Google App Engine

Что такое Google App Engine

В 2008 году компания Google представила свое решение для разработчиков облачных приложений. Сервис получил название Google App Engine (GAE). С его помощью разработчики могут создавать веб-приложения на языках Python и Java (или на любом языке, реализованном в JVM) на основе инфраструктуры Google.

Сервис заинтересовал множество разработчиков, так как получил множество преимуществ, среди которых возможность бесплатно размещать приложения в рамках ежедневных квот, выделяемых Google. Следует отметить, что эти квоты были достаточно большими, поэтому чтобы выйти за их пределы, приложение должно было или потреблять действительно большие ресурсы, либо быть достаточно популярным. Со временем список возможностей и API, предоставляемых GAE увеличивался и дорабатывался и сейчас в него входят:

Однако у GAE были и остаются некоторые ограничения, среди которых следует выделить ограниченность в использовании библиотек языков Java и Python. Google старается сделать свой сервис максимально надежным и безоспасным, поэтому некоторые стандартные функции языков, такие как работа с файлами или потоками ОС невозможна в рамках GAE. Также достаточно непривычной для программиста может оказаться работа с хранилищем данных GAE (Datastore), основанном на внутренней системе Google BigTable. GAE Datastore реализует избыточное безопасное хранение данных на своих серверах. Пользователю нет необходимости думать о защите данных - инфраструктура GAE позаботится обо всем сама. Однако поскольку это хранилище данных нереляционно, то нет встроенных возможностей объединения таблиц (SQL JOIN), что может вызвать неудобства при работе с множеством связанных данных и необходимости их групповой обработки.

Мой опыт с GAE

Я заинтересовался Google App Engine так как до этого с интересом изучал язык Python, а GAE оказался отличным способом бесплатно попробовать силы в веб-программировании. Первым приложением, написанным на GAE стал небольшой сервер подсчета статистики результатов компьютерных игр, который необходимо было сделать в рамках университетского курса "Программирование мобильных устройств". Затем попробовал сделать еще несколько проектов, с помощью которых познакомился с возможностями Google App Engine поближе.

Самым серьезным приложением, которое я сделал на этой платформе оказалось веб-приложение-шутка Spycify. После загрузки фотографии программа, с помощью сервиса поиска лиц face.com, находит на полученном изображении лица и делает из них шпиона - пририсовывает очки и усы.

Логотип Spycify

Spycify — веб-приложение на платформе Google App Engine

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

Тестирование приложений в облаке

Было время, когда я не вообще не делал никакого тестирования. И Spycify было начат и достигнутых первого релиза с количеством тестов равным нулю. Эти первые версии работали, но содержали множество досадных ошибок. Единственное, что мне оставалось, это провести большой рефакторинг всего приложения

Пример работы Spycify

Пример работы Spycify

Предыдущие версии проверялись вручную. Я запускал сервер разработчика, проходил по всем измененным функциям, чтобы проверить они работают. В некоторых случаях этого было недостаточно из-за ограничений GAE. Приходилось загружать приложение на «боевой» сервер и проверять там. Вручную, разумеется. Широкомасштабная переработка стала отличным поводом начать использовать автоматическое тестирование. Больше никаких ручных проверок и самоповторения

При переходе на новую работу в 42cc я столкнулся с Test driven development (TDD). Я не могу сказать, что я большой поклонник такого подхода, но он имеет свои преимущества. Я решил, что не будет писать тест на каждую строчку кода. Юниттесты для основных частей модели данных и пара функциональных тестов для основного рабочего процесса.

Установка

Тестирование приложений GAE требует соответствующих инструментов. В первую очередь надо хороший способ для запуска тестов. Плагин nose-gae для nosetest прекрасно работает. Он делает изолированную среду, позволяя в тестах использовать ресурсы, недоступные в Google App Engine архитектурно - работу с файлами, потоки и т.д. Чтобы установить его, необходимо в консоли выполнить:

pip install NoseGAE

Для запуска тестов:

nosetests -v --with-gae

Модульное тестирование

Это делается с помощью обычных пакетов модульного тестирования unittest и пакета для создания заглушек mockup. Так как я не собирался проверять подключение к face.com, которые я использую для обнаружения лица, я сохранил JSON-выход для одной из фотографий. В тесте я подставляю этот JSON в модель и запускаю метод, который анализирует JSON, отвергает результаты распознавания с низким уровнем уверенности (если face.com выдает правдоподобность распознавания 50%, лучше оставить этот результат, ничего с ним не делая. Такие результаты часто получают складки на рубашках, другие странные вещи) находит координаты для очков и усов. Фактической работы с участием хранилища не происходит.

Таким образом мои тесты загружают входные данные и проверяют, что данные получены, проанализированы и задействованы правильно.

В этом тесте нет ничего особенного на самом деле. Но они мне помогли найти много синтаксических ошибок, неправильно импорта и других ошибок. Поскольку я работал на Spycify по выходным, могу сказать, что не запускал тестового сервера с ним около месяца. Таким образом юниттесты помогли мне понять, что мои модели, вообще говоря, работают.

Функциональные тесты

Первый функциональный тест должен был проверить, что фотографии могут быть загружены, затем обработаны и сервер сгенерировал правильный ответ. Поскольку я не собираюсь анализировать полученное изображение - проверить, что мы не попали на одну из страниц ошибок. Реализация этого заняла немного больше времени GAE SDK не имеет тестирования инфраструктуры, как, например, Django. Таким образом, вы должны создать собственное приложение WSGI и работать с ним. Я воспользовался поиском, чтобы увидеть, как люди решают эту задачу.

Это часть моего test_http.py, который проверяет работу Spycify:


import os
import re

from webtest import TestApp
from spycify.main import application

from wsgiproxy.exactproxy import proxy_exact_request

SERVER_NAME = os.environ['SERVER_NAME']
SERVER_PORT = os.environ['SERVER_PORT']

app = TestApp(proxy_exact_request, extra_environ={
                                    'wsgi.scheme': 'http',
                                    'SERVER_NAME': SERVER_NAME,
                                    'SERVER_PORT': SERVER_PORT,
                                    'HTTP_HOST': '%s:%s' % (SERVER_NAME,SERVER_PORT)
                                  })

def test_index():
    response = app.get('/')
    response.mustcontain('Spycify',
                         '<form',
                        )

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

Тестирование загрузки файлов

Сначала мне казалось, что с тестированием загрузки файлов меня ожидает масса неприятностей. Но это заработало настолько хорошо, насколько я мог ожидать. Пакет Webtest, который используется для функционального тестирования, не работает с JavaScript, поэтому в тесте нам придется выполнять все AJAX-запросы вручную. Например, вы должны получить новые URL для загрузки каждого файла. Сейчас это реализовано с помощью отдельного запроса, который возвращает URL для загрузки. Это URL скрипт вставляет в поле action формы загрузки.


response = app.get('/upload_url')
url_pattern = re.compile(r'http\:\/\/\w+\:\d+(\/.*)')
upload_url = url_pattern.match(response.body).groups()[0]

filename = 'tests/ivan.jpg'
response = app.post(upload_url,
        {'img_type': 'photo'},
         upload_files=(('file', filename),
)) 

Здесь мы сначала делаем запрос, чтобы получить URL. Затем берем последнюю часть URL (без имени сервера и порта). А затем выполняем post-запрос, передавая имя файла, который будет загружен в приложение. Все очень просто!

Следующую страницу также нужно специально обрабатывать. Чтобы соответствовать правилу «показать пользователю какой-либо результат как можно скорее» я делаю всю обработку изображения в фоновом режиме, асинхронно. Когда пользователь нажимает «Отправить» его фото загружается на сервер, где он получает уникальный номер. Сразу после этого пользователь перенаправляется на страницу, содержащую в адресе это число, где он видит сообщение «Загрузка...». На этой странице автоматически отправляется запрос на сервер, который запускает обработку фотографий. Когда обработка выполнена сервер посылает ответ клиенту, содержащий ссылку на изображение и другие элементы страницы.

Поэтому, чтобы это заработало, мы должны извлечь уникальный номер фотографии, используя который выполнить все запросы, которые в браузере выполняет JavaScript, вручную. Когда все они выполнены, мы получаем последний ответа от сервера, в котором остается проверить только наличие тега IMG.

Успешный результат тестирования

Тестирование выполнено, все тесты прошли успешно

Это в основном все тестирования у меня на данный момент. Следующий тест будет добавлена ​​в новых ошибок будет найдено или новые возможности, реализованные.

В таком способе тестирования есть один существенный недостаток. После прохождения всех тестов база данных не очищается. Однако для данного приложения на самом деле, это не критично, поэтому можно обойтись без такой функции. В то же время, для многих других типов приложений, стоит реализовать способ сделать это. Для этого уже должны быть некоторые заготовки, оставленные разработчиками.

И в завершение видео от самих разработчиков, записанное на конференции Google IO, о методах тестирования на Google App Engine с использованием языка Java.

Выводы

Тестирование приложений, безусловно, отнимает время во время разработки. Приложения на GAE до недавнего времени не имели специальных механизмов или готовых фреймворков для более удобного тестирования. Но наличие даже самого скромного набора тестов снижает цену исправления ошибок в будущем, так как необходимость тестирования, кроме собственно проверки работоспособности кода, способствует написанию менее связного (loosely coupled), более абстрактного кода.

Кроме того платформа GAE постоянно развивается, появляются вспомогательные библиотеки, облегчающие написание тестов. Растет сообщество программистов, использующих эту технологию и готовых поделится своим опытом.

Литература

  1. Объем европейского рынка облачных вычислений достигнет 35 млрд евро [Электронный ресурс]. — Режим доступа: http://www.dailycomm.ru/m/13768/
  2. Что такое Google App Engine? [Электронный ресурс]. — Режим доступа: http://code.google.com/intl/ru/appengine/docs/whatisgoogleappengine.html
  3. Testing Google App Engine application [Электронный ресурс]. — Режим доступа: http://blog.brabadu.com/2011/02/testing-google-app-engine-application.html
  4. Testing Google App Engine sites [Электронный ресурс]. — Режим доступа: http://farmdev.com/thoughts/45/testing-google-app-engine-sites/