[Из песочницы] Как я мучал Selenium тесты для GAE Django и к чему пришел в итоге

Предистория Однажды на проекте, написанном на GAE Django, понадобилось реализовать тестирование с помощью Selenium. К сожалению, найти готовый инструмента для этого не удалось. Поиски по просторам интернета не дали положительных результатов.Поворот не туда Первое, что пришло в голову — это запускать сервер в setUp с помощью subprocess.Popen и завершать его в teardown методе self.server_process.terminate () На первый взгляд — рабочее решение. Первый вопрос, который возник: Как заставить запущенный инстанс сервера и testbed использовать одну базу и сервисы? При запуске сервера указываем, какую БД использовать:

--use_sqlite --datastore_path=/full/path/to/sqlite_db Говорим testbed с каким app_id и из какой базой запускается тестовый сервер: self.testbed.setup_env (app_id='some-app-id') self.testbed.init_datastore_v3_stub (use_sqlite=True, datastore_file='/full/path/to/sqlite_db') Кажется, готово, но возникает проблема из-за, так сказать, конфликтов testbed и нашего сервера. Сервер запускает необходимые ему сервисы, и testbed делает то-же самое. В общем, «кто последний тот и папочка». Никак не удавалось получить нормально рабочую базу. В итоге, после подбора порядка инициализации у меня получилось и первый тест прошел успешно. Казалось, победа близка, но меня не переставала покидать мысль: чем дальше я продвигаюсь с этими тестами, тем глубже погружаюсь в пучину «грязных» решений.Необходимо чистить базу В teardown () методе вызывался testbed.deactivate () и test_server.terminate ().База не очищалась. Добавление --clear_datastore к команде запуска сервера все ломало во время инициализации. Удаление всех данных из базы и очищение кеша вручную также вызывало конфликты. db.delete (db.Query ()) memcache.flush_all () Пришлось добавить удаление файла базы os.remove, но и это не решало всех проблем.С каждым решением одной проблемы возникала другая. Руки опускались, становилось все страшнее, от того что я пишу. Лег спать.

Все гениальное — просто Не так уж и просто, но проще, чем описанное выше. Спасибо моему коллеге, который натолкнул на мысль использовать LiveServerTestCase. Это стало спасением.Базовый класс, который активирует testbed и деактивирует его по окончанию всего тесткейса:

class GAELiveServerTestCase (LiveServerTestCase): @classmethod def setUpClass (cls): cls.testbed = testbed.Testbed () cls.testbed.activate () cls.testbed.init_datastore_v3_stub () cls.testbed.init_memcache_stub () cls.testbed.init_channel_stub () cls.testbed.init_urlfetch_stub () cls.testbed.init_user_stub () super (GAELiveServerTestCase, cls).setUpClass ()

@classmethod def tearDownClass (cls): cls.testbed.deactivate () super (GAELiveServerTestCase, cls).tearDownClass () Собственно класс selenium тестов.

class SeleniumTestCase (GAELiveServerTestCase): def setUp (self): self.driver = webdriver.Chrome ()

def tearDown (self): self.driver.close () self.driver.quit () db.delete (db.Query ()) memcache.flush_all () И один важный момент.

LiveServerTestCase не запускает инстансов GAE типа _ah. Channels были необходимы. Подсмотрев в одном проекте и покопав gae-шные сорцы, нарисовалась дополнительная вьюха, можно сказать, со скопированной логикой из gae для jsapi и connect, disconnect, poll сигналов:

from google.appengine.tools.devappserver2.channel import _JSAPI_PATH

def channel_stub_view (request, page): params = request.REQUEST

if page == 'jsapi': return HttpResponse (content=open (_JSAPI_PATH).read (), content_type='text/javascript')

elif page == 'dev': command = params.get ('command', None) token = params.get ('channel', None) if command is None or token is None: return HttpResponse (status=400)

stub = apiproxy_stub_map.apiproxy.GetStub ('channel') try: stub.connect_channel (token) except (channel_service_stub.InvalidTokenError, channel_service_stub.TokenTimedOutError): return HttpResponse (status=401)

client_id = stub.validate_token_and_extract_client_id (token) if command == 'connect': return HttpResponse (content='1', content_type='text/plain') elif command == 'poll': message = stub.pop_first_message (token) if message is not None: return HttpResponse (content=message, content_type='application/json') return HttpResponse () Сделал доступной только во время тестов:

if settings.TESTING: urlpatterns += patterns ('', url (r'^_ah/channel/(? P.*)$', 'channel_stub_view'), ) Оставалась одна проблема, когда должен происходить disconnect (например страница закрылась), testbed не понимал этого. Решил проблему инициализацией channel_stub при каждом открытии страницы:

class SeleniumTestCase (GAELiveServerTestCase): def setUp (self): self.driver = webdriver.Chrome ()

def tearDown (self): self.driver.quit () db.delete (db.Query ()) memcache.flush_all ()

def open_url (self, url): self.get (urljoin (self.live_server_url, url)) self.testbed.init_channel_stub () P.S. Написал пост в надежде, что кому-то поможет это решение, и он не потратит уйму времени и нервов на подобную задачу.

© Habrahabr.ru