Чтение ответа cherrypy в виде байтов / строки после миграции с py2 на py3 - PullRequest
0 голосов
/ 24 января 2019

Я только что перенес вишневое приложение с 2.7 на python (работает на 3.6).У меня была куча тестов, настроенных ранее на основе этого рецепта .Суть рецепта состоит в том, чтобы эмулировать сеть и выполнять тестирование модулей на отдельных конечных точках.

Теперь мой сервер, по-видимому, работает нормально.Однако, если я запускаю тестовые модули, они в основном возвращают ошибки в py3 (все проходят в py2), что, похоже, связано с тем, что ответ находится в байтах (в py3), а не в строке (в py2).

A test response
    ======================================================================
    FAIL: test_index (__main__.EndpointTests)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/home/anonymous/PycharmProjects/server-py3/tests/test_DServer.py", line 67, in test_index

        self.assertEqual(response.body, ['Hello World!'])
    AssertionError: Lists differ: [b'Hello World!'] != ['Hello World!']

    First differing element 0:
    b'Hello World!'
    'Hello World!'

    - [b'Hello World!']
    ?  -

    + ['Hello World!']

Аналогично:

======================================================================
FAIL: test_valid_login (__main__.EndpointTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/anonymous/PycharmProjects/server-py3/tests/test_DServer.py", line 73, in test_valid_login
    self.assertEqual(response.output_status, '200 OK')
AssertionError: b'200 OK' != '200 OK'

Я знаю, что поведение несколько отличается в отношении байтов (например, как объяснено здесь ).

На самом деле 2 вопроса:

Какой лучший способ справиться с этим в моем тесте?Нужно ли ставить b перед каждой строкой, которая указана в ответе сервера?

Быстрое тестирование на сервере, похоже, показывает, что оно работает.Однако могу ли я быть укушен этой проблемой другим способом?Любые слова мудрости в отношении других ошибок с вишней и переходом на py3?

После этого я не поддерживаю py2, я могу выполнить чистую миграцию.

1 Ответ

0 голосов
/ 24 января 2019

Нашли.

Изменения должны быть сделаны в cptestcase. Суть проблемы заключается в том, что этот рецепт в некоторой степени зависит от внутренней работы Cherrypy, 2to3 (инструмент, который я использовал для выполнения основной работы по миграции) не мог достаточно хорошо управлять деталями, чтобы он понравился cp.

Суть в том, что вместо io.StringIO (что по умолчанию предусмотрено 2to3) вам нужно переключиться на io.BytesIO. Поэтому предыдущие вызовы StringIO (данные) должны быть BytesIO (данные). Дело в том, что внутренне cp ожидает, что эти строки / байты (так как py2 на самом деле не имеет никакого значения между 2) будут настоящими байтами (потому что py3 на самом деле дифференцируется). И да, в самом тесте при создании утверждений вы должны либо преобразовать response.output_status & response.body из байтов в строки, либо сравнить их с байтами, например:

self.assertEqual(response.output_status, b'200 OK')

Однако строка запроса (для GET) все еще должна оставаться строкой.

Вот полное редактирование кода, который работал для меня:

from io import BytesIO
import unittest
import urllib.request, urllib.parse, urllib.error

import cherrypy

cherrypy.config.update({'environment': "test_suite"})
cherrypy.server.unsubscribe()

local = cherrypy.lib.httputil.Host('127.0.0.1', 50000, "")
remote = cherrypy.lib.httputil.Host('127.0.0.1', 50001, "")

__all__ = ['BaseCherryPyTestCase']

class BaseCherryPyTestCase(unittest.TestCase):
    def request(self, path='/', method='GET', app_path='', scheme='http',
                proto='HTTP/1.1', data=None, headers=None, **kwargs):

        h = {'Host': '127.0.0.1'}

        if headers is not None:
            h.update(headers)

        if method in ('POST', 'PUT') and not data:
            data = urllib.parse.urlencode(kwargs).encode('utf-8')
            kwargs = None
            h['content-type'] = 'application/x-www-form-urlencoded'

        qs = None
        if kwargs:
            qs = urllib.parse.urlencode(kwargs)

        fd = None
        if data is not None:
            h['content-length'] = '%d' % len(data.decode('utf-8'))
            fd = BytesIO(data)

        app = cherrypy.tree.apps.get(app_path)
        if not app:
            raise AssertionError("No application mounted at '%s'" % app_path)

        app.release_serving()

        request, response = app.get_serving(local, remote, scheme, proto)
        try:
            h = [(k, v) for k, v in h.items()]
            response = request.run(method, path, qs, proto, h, fd)
        finally:
            if fd:
                fd.close()
                fd = None

        if response.output_status.startswith(b'500'):
            print(response.body)
            raise AssertionError("Unexpected error")

        response.collapse_body()
        return response
...