작성자: yagur Rev : 5
테스트 의존성의 함정(Trap of Test Dependencies)
1. 규칙
테스트가 테스트 대상의 디자인과 대칭되도록 프레임워크가 구성되어 있는것을 앞서 기술했다. 복잡함엔 대가가 따르기 마련이다. 엔트로피나 복잡도와 같은 주제를 앞서 다룬것은 이때문이다. 안타깝게도 모든 객체의 의존성을 관리하기 위한 수많은 원칙과 방법들을 익히는것은 OOP 신자들의 운명이다.
진화된 테스트 프레임워크는 테스트 개체간에 수많은 의존성을 부여할수 있게 되었다. 이제 DAG의 트리-순회(tree traversal)는 순차적인것과는 전혀 거리가 멀어졌다. 이는 두번째 표인 "테스트 그래프의 예"에서 순서를 보면 알수있다.
그리고 의존성이 가질수 있는 함정을 간과하고 넘어가면 안된다. 그중하나는 원형 의존성(circular dependecy)이다. 단어의 사전적 의미대로 원을 그리는 의존성을 지닌 다이어그램을 상상하면 된다. 비슷한 예는 순환 집합(Cyclic Aggregation) 관계가 있다.
집합관계는 문제가 있어보이지만 테스트 의존 관계도 같은 문제를 갖는 것일까? 상호 참조가 권장되진 않지만 논리적 오류라고 볼순 없다. 하지만 테스트의 의존 노드는 의존 대상의 결과를 선행 조건(precondition)으로 삼는 계약을 하고 있다. 이 속성때문에 원형 의존 관계는 문제가 될수있다. 물론 계약 조건을 바꿀수 있으나 그렇게 되면 테스트를 구성하는데 제약이 따르며, 테스트간 의존 관계를 부여함으로 얻어지는 이점이 상당히 떨어지게 된다. 그것은 아래의 몇가지 규칙에 연관되어있다.
테스트 DAG에 의존그래프(Dependency Graph)를 추가했을때, 트리-순회(Tree Traversal)와 실행순서에 몇가지 흥미로운 흥미로운 규칙을 발견할수 있었다.(누군가 이미 정의했을듯 하지만 아직은 본적이 없어, 신조어를 쓸수밖에 없었다.). 아래의 테스트 그래프를 보자.
* 바텀업 구조이므로 하위 테스트부터 완료후 검증해, 상위를 노드의 성공여부를 나타낸다고 생각하고 그래프를 보면 된다.
* 직선은 테스트의 트리구조를 연결하는것이고 점선과 화살표는 의존관계를 나타내는것이다.
* 원안의 번호는 트리-순회에 따라 테스트가 완료된 순서를 나타낸다.
* 원안의 번호는 트리-순회에 따라 테스트가 완료된 순서를 나타낸다.
첫번째 그래프는 의존성없이 구성된 테스트 그래프의 실행 순서이다. 그리고 두번째는 의존성은 있지만 테스트 와료 순서가 변경되지 않은 그래프, 세번째는 의존성이 있고 테스트 완료 순서가 변경된 경우이다.
두번째 그래프에서 발견할수 있는 사실은 의존성이 우에서 좌로(Right To Left) 향해있는경우, 테스트 트리-순회(Tree traversal)에 따른 테스트 실행 완료 순서가 바뀌지 않는 규칙이 있다.
반면에 세번째와 같이 의존성은 하나지만 의존성의 방향이 좌에서 우로(Left To Right) 향해있는경우, 테스트 트리-순회(Tree traversal)에 따른 테스트 완료 순서가 바뀌어 버리는것을 볼수 있다.
이제부터 이를 L2R(좌에서 우), R2L(우에서 좌)이라는 약자를 쓰겠다.
R2L를 철저히 지킨다면 의존성이 테스트 실행순서에 영향을 미칠 이유는 없다. 그러므로 의존성에 특별한 의미를 두지 않을수도 있다. 하지만 그런 디자인은 세상에 거의 없다고 봐도 된다. 불행히도 지금까지 봐온 의존성은 팔방으로 뻗었다. 즉 테스트 그래프의 실행순서는 복잡하게 얽히기 마련이다. 물론 이는 프레임워크를 만들어 놓으면 해결되니 걱정할 필요는 없다.
예전에 예를 들었던 Trent 900을 한 예를들어보겠다.(아직 이들 규칙에 모호함을 느낀다면 아래의 예를 보면된다.)
more..
2. 원형 의존의 함정
R2L만이 아니라 L2R도 존재함으로 원형 의존성은 생길수 있다. 그리고 이둘은 서로 다른 성질을 지니고 있다, 하나(R2L)는 순서를 유지시키는 것이고 다른 하나(L2R)는 순서를 바꾸는 것이다. 그리고 이 두 가지 연결의 공존은 무한 트리-순회를 초래한다.
L2R이 없다면 원형이 될수 없다. 자기 자신을 의존하지 않는 이상말이다. 잘못된 의존성 연결은 무한루프에 빠지게 될수 있다. 그러므로 몇가지 안전장치가 필요하다. 일단 이 안정장치는 테스트 상태 추가를 통해 구현될수 있다. 일단 테스트 트리-순회(Tree traversal) 프로세스의 일부를 보자.
아래는 테스트 노드가 테스트 수행을 위해 자신의 의존 노드들을 검증하는 시퀀스 다이어그램이다.
테스트 의존성 검사 시퀀스 다이어그램
현제 기술하고 있는 프레임워크는 그것의 부모 세대인 Kent Beck의 xUnit과 같은 Case & Suite에서 상속받은것이 있다. 테스트 상태 비교인 "IsTested()"와 테스트의 주 관심사인 Test() 멤버함수같은 것들이다.(xUnit은 WasRun(), Run()으로 이들을 구현하고있다.) 테스트의 상태를 저장하는것은 중복실행을 방지해줄수있기 때문에 필요하다. 중복실행을 방지한다는것은 원형 의존에 의한 무한 순회의 고리를 끊을수 있는 임시 해결책이 며 안전장치이다.1번째 Test Node : 테스트 호출 대상이며, 테스트 DAG를 트리 순회(tree-traverse)중 실행되는 테스트
2번째 Dependencies : Test Node의 의존성 컨테이너
3번째 a dependency test : 2번째 Depedencies 컨테이너가 가지고 있는 의존성 테스트
4번째 Dependencies : 3번째 test의 의존성 컨테이너
2번째 Dependencies : Test Node의 의존성 컨테이너
3번째 a dependency test : 2번째 Depedencies 컨테이너가 가지고 있는 의존성 테스트
4번째 Dependencies : 3번째 test의 의존성 컨테이너
시퀀스 다이어그램의 "1:의존성 확인" 이전에 '현제 테스트가 진행중이라면 테스트를 실패로 만드는 메소드'를 삽입하면 무한 순회는 막을수 있다. 하지만 이는 임시 방편일 뿐이다.
상호 참조, 혹 상호 대화를 하는 객체를 테스트 하는 테스트 패턴이 존재한다. 이를 셀프션트(Self Shunt)라고 하는데, 이는 모의 객체(Mock Object)와 연관이 있다. 이에 관해서는 차후에 다루겠다. 가장 좋은건 근본적인 테스트 방법 혹은 디자인을 개선하여 이를 해결해 나가는 것이다.
이 Self Shunt는 객체지향의 OCP원칙과 관련이 깊다. 객체가 확장을 허용하기 위한 추상화, 그리고 추상화 된것과의 느슨한 결합은 우리가 자주 쓰는 것들이다. 구체화 된것끼리 의존 하던 1,2 번 테스트가 모의 객체를 통해 서로 테스트 의존성을 끊고 있다. 그 모의 객체는 대화 상대를 대신하는것이며, 실체가 없는것에 의존한다는 점에서 추상화 개념과 맞물려 있다. 그리고 이것은 Flip-Flop 패턴과 유사하다.
3. 부모와 자식의 함정
상위 테스트와 하위 테스트가 포함관계에 놓이는것도 일종의 의존성 관계를 갖는것이다. 그리고 자식이 부모에 의존성을 둔다면 어떻게 될까?
상위 테스트는 하위 테스트의 성공을 자신의 검증 조건으로 계약하고 있다. 이는 바텀업 방식이기 때문이다. 자식이 자신을 검증하기 위해서 부모를 검증한다면, 여기서 원형 의존성과 똑같은 함정에 빠지게 된다. 서로가 서로를 선행조건으로 내세우기 때문이다. 이것도 일종의 원형 의존성이라 할수 있다.
'개발 > 수필' 카테고리의 다른 글
| 테스트와 장인 - 7 (0) | 2008/03/12 |
|---|---|
| 객체 지향 소프트웨어 일주 - 1 (0) | 2008/03/11 |
| 테스트와 장인 - 6 (2) | 2008/02/25 |
| 테스트와 장인 - 5 (0) | 2008/02/22 |
| 테스트와 장인 - 4 (2) | 2008/02/21 |
| 테스트와 장인 - 3 (6) | 2008/02/19 |








Recent Comment