Используя CI Gitlab для автоматического запуска модульных тестов, я столкнулся с проблемой, когда конкретный контрольный пример будет иногда терпеть неудачу только на gitlab-runner и никогда в моей среде разработки. Когда что-то не работает детерминистически, я волнуюсь и хочу докопаться до сути.
Вот тестовый пример, обратите внимание, что время имеет решающее значение:
def test_get_file_url(self):
# 1. get pre-signed URL to file
expiration = 3
url = s3.get_file_url(file_default.key, expires=expiration)
# 2. download file contents, make sure contents match expectation
response = requests.get(url)
content = response.content
self.assertEqual(content, file_default.bytes)
# 3. sleep long enough for URL to expire
time_to_sleep = expiration * 4
start_time = time.time()
time.sleep(time_to_sleep)
end_time = time.time()
time_slept = end_time - start_time
print("time slept: {} seconds, expected: {} seconds".format(time_slept, time_to_sleep))
# 4. make request, expecting URL to have expired
response2 = requests.get(url)
content2 = response2.content
self.assertIn("Request has expired", str(content2)) # THIS FAILS (sometimes)
Некоторое объяснение:
s3.get_file_url(...)
возвращает предварительно подписанный URL-адрес для файла на AWS S3, срок действия которого истекает через указанные 3 секунды. Учетные данные AWS и имя сегмента S3 предоставляются в методе / s3-модуле.
- Загрузите файл по этому URL и убедитесь, что файл содержит то, что он должен содержать.
- Спит достаточно долго (намного дольше, чем необходимо), чтобы срок действия URL истек.
- Сделайте запрос на тот же URL, ожидая, что срок действия URL истек. Здесь тестовый случай иногда завершается неудачей. Я ожидаю, что XML-содержимое содержит строку «Запрос истек», но вместо этого я по-прежнему получаю фактическое содержимое файла.
Для ясности: при сбое конвейера я нажимаю кнопку повтора в веб-интерфейсе gitlab, и он может работать - никакого кода не изменилось!
Что я сделал:
- увеличить
time_to_sleep = expiration * 10
безрезультатно
- измерение фактического
time_slept
, как видно из кода выше (вывод подтверждает 12+ секунд)
.gitlab-ci.yml:
image: "python:3.6.4"
before_script:
- pip3 install pipenv
- pipenv install --dev
test:
script:
- TESTING=True pipenv run coverage run --source=myapp -m unittest discover -s myapp
- pipenv run coverage report | grep TOTAL
tags:
- unittesting
Некоторые (очень сомнительные) предположения:
- пакет
requests
каким-то образом возвращает кэшированный ответ на тот же URL
- как бы это отключить?
- почему это не всегда происходит?
- 2-й
requests.get(url)
фактически каким-то образом выполняется до того, как time.sleep(time_to_sleep)
возвращает:
- может
time.sleep()
как-то быть неблокирующим?
- мои измерения не работают должным образом
Я понимаю, что это может быть очень специфическая проблема со многими возможными источниками ошибок. Надеюсь, что другой взгляд или кто-то, у кого была похожая проблема, сможет направить меня в правильном направлении.