2016-12-24 1 views
10

우리는 Python/Django 어플리케이션을위한 1500 가지 이상의 테스트를 가진 커다란 테스트 코드베이스를 가지고 있습니다. 대부분의 테스트에서는 factory-boy을 사용하여 프로젝트 모델에 대한 데이터를 생성합니다.테스트 커플 링 자동 검출

현재 nose test runner을 사용 중이지만 py.test으로 전환 할 수 있습니다.

문제는 때때로 테스트의 일부를 실행할 때 모든 테스트 또는 이러한 테스트를 개별적으로 실행할 때 재생되지 않는 예기치 않은 테스트 실패가 발생합니다.

실제로는 테스트가 결합 된 것 같습니다..

질문 : 프로젝트에서 결합 테스트를 자동으로 감지 할 수 있습니까?

내 생각은 모든 테스트를 다른 임의의 조합 또는 순서로 실행하여 실패를보고하는 것입니다. nose 또는 py.test이 도움이 될 수 있습니까?

답변

4

확실한 대답을 얻으려면 각 테스트를 나머지 테스트와 완전히 분리하여 실행해야합니다.

내가 사용하는 pytest을 사용하면 --collect-only으로 먼저 실행 한 스크립트를 구현 한 다음 반환 된 테스트 노드 ID를 사용하여 각각에 대해 pytest 개별 실행을 시작할 수 있습니다. 1500 개의 테스트를 수행하는 동안 시간이 걸릴 수 있지만 각 개별 테스트 사이에 시스템의 상태를 완전히 다시 작성하는 한 작업을 수행해야합니다.

대략적인 답을 얻으려면 테스트를 임의 순서로 실행하고 실패한 횟수를 확인하십시오. pytest-randomly가 더 성숙처럼 보이는 심지어 seed 매개 변수를 수락하여 특정 순서를 반복 지원, 두에서 https://pypi.python.org/pypi/pytest-randomly/ https://pypi.python.org/pypi/pytest-random/

: pytest-randomlypytest-random - 나는 비슷한 질문이 최근에, 그래서 두 pytest 플러그인을 시도했다 .

이러한 플러그인은 테스트 순서를 랜덤 화하는 데 적합하지만 대규모 테스트 스위트의 경우 완전하지 않은 테스트는 실패 할 수있는 테스트가 너무 많아서 어디서부터 시작해야할지 모르기 때문에 매우 효과적이지 않을 수 있습니다.

필자는 테스트가 임의로 (모듈, 패키지 또는 전역) 순서를 변경할 수있는 수준을 제어 할 수있는 자체 플러그인을 작성했습니다. pytest-random-order : https://pypi.python.org/pypi/pytest-random-order/

업데이트입니다. 귀하의 질문에 개별적으로 테스트를 실행할 때 오류를 재현 할 수 없다고 말합니다. 개별 테스트 실행을 위해 환경을 완전히 재구성하지는 못했을 수 있습니다. 일부 검사는 상태가 더러운 상태로 유지되는 것이 좋습니다. 필요에 따라 환경을 설정하고 후속 테스트에서 발생할 수있는 성능 오버 헤드 또는이를 수행하는 부담으로 인해 이후에 정리하지 않아도되는 것은 각 테스트 케이스의 책임입니다.

테스트 X가 더 큰 테스트 제품군의 일부로 실패한 다음 개별적으로 실행될 때 실패하지 않으면이 테스트 X는 테스트 환경을 설정하는 데 충분하지 않습니다.

+0

안녕하세요 Jāzeps, 여기에서 당신을보고 있습니다! 'pytest-random-order'가 좋아 보인다. 나는 그것을 조사 할 것이다. – wim

+0

하하, 안녕하세요. @wim thanks! – jbasko

2

nosetests 프레임 워크를 이미 사용하고 있으므로 nose-randomly (https://pypi.python.org/pypi/nose-randomly)을 사용하면 임의의 순서로 테스트 사례를 실행할 수 있습니다.

당신이 nose-randomly와 코 테스트를 실행할 때마다, 각 실행은 당신이 반복 같은 순서로 테스트를 실행에 사용할 수있는 임의 초기 태그입니다.

따라서이 플러그인을 여러 번 사용하여 테스트 케이스를 실행하고 임의의 씨앗을 기록하십시오. 특정 주문에 오류가 발생하면 항상 임의의 시드를 사용하여 오류를 재현 할 수 있습니다.

2^1500-1 1500 개의 테스트 조합을 모두 실행하지 않는 이상 이상적으로 테스트 종속성과 실패를 식별 할 수 없습니다.

그래서 테스트를 실행할 때 무작위로 실행되도록 설정하는 습관을 들여야합니다. 어떤 시점에서 가능한 한 많은 실패를 잡을 때까지 실패를 명중하고 계속 실행합니다.

오류가 제품의 실제 버그를 포착하지 않는 한, 문제를 수정하고 테스트 종속성을 가능한 한 적게 만드는 것이 좋은 습관입니다. 이렇게하면 테스트 결과의 일관성을 유지할 수 있으며 테스트 사례를 독립적으로 실행하고 검증 할 수 있으며 해당 시나리오와 관련된 제품의 품질을 확인할 수 있습니다.

도움이되는 희망은 달성하고자하는 것과 정확히 같은 상황을 달성하기 위해 작업장에서하는 희망입니다.

0

테스트가 환경을 제대로 분해하지 못하는 경우에 발생합니다.

예 : 테스트의 설정 단계에서 테스트 데이터베이스에 일부 개체가 생성되고 일부 파일에 기록되거나 네트워크 연결이 열리는 등 상태가 제대로 재설정되지 않으므로 정보가 전달됩니다. 이후의 테스트는 입력 데이터에 대한 잘못된 가정으로 인해 실패 할 수 있습니다.

테스트 사이의 커플 링에 초점을 맞추기보다는 (위의 경우에는 실행 순서에 따라 다소 의의가 있습니다), 아마도 테스트를 테스트하는 루틴을 실행하는 것이 좋습니다 각 시험의 일상.

원래의 Test 클래스를 래핑하고 teardown 함수를 재정 의하여 테스팅이 주어진 테스트에 맞게 제대로 재설정되었다는 일종의 일반적인 테스트를 포함시켜 수행 할 수 있습니다.

뭔가 같은 : 테스트 파일

class NewTestClass(OriginalTestClass): 
    ... 

    def tearDown(self, *args, **kwargs): 
     super(NewTestClass, self).tearDown(*args, **kwargs) 
     assert self.check_test_env_reset() is True, "IM A POLLUTER" 

그리고 새와 함께 원래의 테스트 클래스의 import 문을 대체 :

# old import statement for OriginalTestClass 
from new_test_class import NewTestclass as OriginalTestClass 

이후에 테스트를 실행하기위한 오류가 발생할한다 오염을 일으키는 것들.

반면에 테스트가 다소 더러운 것을 허용하려면 실패한 테스트에 대한 테스트 환경의 잘못된 설정으로 문제를 확인하십시오.

실패한 테스트는 잘못 작성된 테스트이므로 개별적으로 수정해야합니다.

두 가지 관점은 어느 정도는 음양이고 어느 쪽이든 채택 할 수 있습니다. 가능한 한 후자가 더 강력합니다.

+0

질문의 맥락에서이 지점은 의미가 없습니다. Django'TestCase' 클래스는 [TransactionTestCase'] (https://docs.djangoproject.com/en/1.10/topics/testing/tools/#django.test.TransactionTestCase)로, 테스트간에 테이블을 자릅니다. 해체는 일반적으로 요구되지 않으며 귀하의 제안을 구현하는 것은 단지 시간 낭비 일뿐입니다. – wim

+0

@wim - 이것을 지적 해 주셔서 감사합니다. 나는 좀 더 일반적인 유형의 오염 (문제의 OP 설명이 주어져야 함)과 테스트 DB 자체의 상태에 초점을 맞추기 위해 나의 대답을 수정했다. (표준 Django의 경우 올바른 것이다. 각 테스트는 실제로 테스트가 끝날 때 자동 롤백되는 트랜잭션에서 실행되므로 일반적으로 해체는 필요하지 않습니다. –

+0

이 질문에 답하기 위해 장고에 대한 충분한 경험이 없다고 생각합니다. 'teardown '호출은 작동하지 않습니다. 이름은'tearDown'입니다. 또한'* args, ** kwargs'를 쓰러 뜨리는 것은 일반적으로 찢어지기 위해 요구되지 않지만, 당신이했던 것처럼''super'' 호출에서 그들을 떨어 뜨리는 것은 다중 상속 상황에서 끔찍한 것들을 깨뜨릴 수 있습니다. 그리고 당신은 무언가가 '참'과 동등하다고 주장 할 필요가 거의 없을 것입니다. – wim

1

코 러너와 공장 보이를 사용하는 대형 Django 프로젝트에서 비슷한 문제가 해결되었습니다. 나는 어떻게 자동으로 요청 질문처럼 테스트 커플 링을 감지하는 방법을 말할 수 없다,하지만 난 내 경우에는 커플 링을 일으키는 원인이 된 몇 가지 문제에 대해 이야기 할 수있는 가늠자했습니다의

확인 모든 수입 TestCase을 사용하고 Dtango의 TestCase을 사용하고 unittest의 TestCase을 사용하지 않았는지 확인하십시오. 팀의 일부 개발자가 편리한 자동 가져 오기 기능이있는 PyCharm을 사용하는 경우 실수로 잘못된 위치에서 이름을 쉽게 가져올 수 있습니다. unittest TestCase은 큰 Django 프로젝트의 테스트 스위트에서 기꺼이 돌아가지만 장고 테스트 케이스에있는 좋은 커밋 및 롤백 기능을 얻지 못할 수 있습니다.

는 확인이 supersetUp, tearDown, setUpClass, 또한 tearDownClass 대표를 무시 어떤 테스트 클래스. 나는 이것이 분명하게 들리지만, 잊기가 매우 쉽다는 것을 알고있다.

공장 소년 때문에 변경 가능한 상태로 몰래 들어올 수 있습니다. DB를이 깨끗한

name = factory.Sequence(lambda n: 'alecxe-{0}'.format(n)) 

하더라도 다른 테스트는 사전에 실행 한 경우, 순서가 0에서 시작되지 않을 수 있습니다 같을 공장 시퀀스의 용도,주의. Django 모델의 가치가 factory boy에 의해 만들어 질 때 어떤 값인지에 대한 잘못된 가정으로 단언 할 때 이것은 당신을 물 수도 있습니다.

마찬가지로 기본 키에 대한 가정을 할 수 없습니다. 장르 모델 Potato이 자동 필드에서 키 입력되고 테스트 시작시 행이 Potato이 아니며 공장 소년이 감자를 만들면 setUpPotatoFactory()을 사용했다고 가정합니다. 놀랍게도 기본 키가 1이된다는 보장은 없습니다. 팩토리가 반환 한 인스턴스에 대한 참조를 보유하고 실제 인스턴스에 대한 어설 션을 만들어야합니다.

RelatedFactorySubFactory에도 매우주의하십시오. 공장 소년은 관계를 만족시키기 위해 이전 인스턴스를 선택하는 습관이 있습니다. 이미 존재하는 경우 DB에 있습니다. 즉, 관련 객체가 반복되지 않는 경우가 있습니다. 다른 객체가 setUpClass 또는 조명기에서 생성 된 경우 테스트 순서가 임의이므로 공장에서 선택하거나 생성 한 관련 객체를 예측할 수 없습니다.

Django 모델의 @receiver 장식자가 post_save 또는 pre_save 후크가있는 상황은 공장 출고시 소년에게 올바르게 처리하는 것이 매우 까다 롭습니다. 이전 인스턴스를 잡는 것만으로는 정확하지 않은 경우를 포함하여 관련 객체를보다 잘 제어하려면 공장에서 _generate 클래스 메소드를 재정의하거나 @factory.post_generation 데코레이터를 사용하여 사용자 고유의 후크를 구현하여 직접 세부 사항을 처리해야하는 경우가 있습니다.

관련 문제