시스템유형별/객체 지향

페이퍼요약 - 객체 지향 소프트웨어 테스팅 이슈 by Barbey

grapevine9700 2018. 3. 23. 06:30
반응형

제목: 객체 지향 소프트웨어 테스팅 이슈(The Problematics of Testing Object-Oriented Software)

저자: Stéphane Barbey 1, 스위스

문서유형: 컨퍼런스 페이퍼( 18페이지), 1994

 

객체 지향 패러다임의 고유 특성(encapsulation, inheritance, polymorphism)에 관련된 테스팅 이슈를 기술한 자료(단위 테스트에 중점)



소프트웨어 아키텍쳐 차이: 절차지향(procedure-oriented) vs. 객체지향(Object-oriented)

  • 구조적 아키텍쳐에서 프로그램 기본 단위는 서브프로그램(subprogram)이며, 서브프로그램들은 패러미터 전달(passing parameters) 또는 글로벌 변수(global variables)를 통해 서로 커뮤니케이션 함
  • 객체 지향 아키텍쳐는 오브젝트와 클래스로 구성됨. 오브젝트는 자신의 상태를 정의하는 속성(feature)과 동작을 정의하는 오퍼레이션(operations)으로 구성되며, 클래스는 오브젝트 인스턴스가 생성되는 템플리트
  • 기존 절차 지향 프로그램의 테스트 기본 단위(The basic unit of test)가 서브프로그램인 반면 객체 지향 시스템의 기본 테스트 단위는 클래스(클래스 내의 개별 오퍼레이션을 별도로 테스트 할 수 없으며, 클래스는 인스턴스 생성을 통하여서만 테스트가 가능)
  • 클래스의 오퍼레이션이 호출되는 순차적 순서(sequential order)가 존재하지 않으므로 기존 콘트롤 플로우 분석 기법(Control flow analysis techniques)들을 그대로 적용하지 못함. 하지만 직접적이지는 못해도 전통적인 테스트 기법들을 일부 테스트 단계에 여전히 적용 가능
  • 객체 지향 시스템 테스팅은 오브젝트 상태에 중점을 두어야 하며, 오브젝트의 오퍼레이션이 실행될 때 오브젝트 상태가 일관성을 유지하는지를 체크해야 한다.


테스트 분야에 종사하는 사람이라면 대부분이 알만큼 유명한 G. Myers의 책 서두에 나오는 삼각형 예제(세 정수 값을 받아 이등변 삼각형인지, 부등변 삼각형인지, 정삼각형인지 판단)를 기존 순차 언어와 객체 지향 언어로 작성하면 대략 아래와 같은 모습

순차 프로그램(Pseudo code)

객체 지향 프로그램(Pseudo code)

Type

Kind: (scalene, isosceles, equilateral, error);

 

function Kind_of (Length1, Length_2, Length_3: Integer): Kind

begin

...

end;

class interface Triangle

inherit Shape is

 

operation Create (Length1, Length_2, Length_3: Integer)

return Triangle;

operation Length_of (Side: Integer range 1..3)

return Integer;

 

enumeration Kind is (scalene, isosceles, equilateral, error);

operation Kind_of return Kind;

 

operation Homothety (Factor: Positive);

operation Similar (A_Triangle: Triangle) return Boolean;

end Triangle.


우측의 객체 지향 프로그램은 다음과 같은 특징을 보인다.

  • 캡슐화(encapsulation): 문제 해결과 직접적인 관계가 없는 Homothety 또는 Similar 같은 것을 포함한 모든 오퍼레이션과 타입이 클래스 내에 캡슐화 됨. Kind_of 오퍼레이션이 별도의 분리된 알고리즘이 아니므로 Triangle 인스턴스를 부르지 않고 해당 오퍼레이션만 따로 테스트가 불가능
  • 정보 은폐(hiding): 클래스 내부 데이터 구조가 감추어져 있으며 여기에 접근할 수 있는 유일한 방법은 Length_of 같은 Interface에 정의된 공용(public) 오퍼레이션을 통해서임
  • 상속(inheritance): Triangle 클래스는 Shape 클래스의 서브 클래스여서 데이터 구조와 동작(오퍼레이션 구현)이 Shape에 의존함(즉, 부모 클래스인 Shape의 특성을 상속 받음)
  • 오퍼레이션 바인딩(operations binding): 인스턴스 생성을 위해 Create 오퍼레이션이 호출되기 전에는 Triangle 오브젝트에 Length_of 또는 Kind_of 오퍼레이션을 적용하는 것이 불가능(tightly bound)


캡슐화(Encapsulation)/은폐(Hiding)

  • Observability 또는 Testability 문제: 오브젝트의 상태를 관측할 수 있는 유일한 방법이 Public 오퍼레이션을 통해서이므로 클래스 데이터 구조에 가시성을 제공하는 Public 오퍼레이션이 존재하지 않는 경우 테스트를 수행하면서 오브젝트 상태의 일관성이나 정확성을 체크하는 것이 어렵게 됨
  • 이 문제에 대한 여러 해결책이 있을 수 있는데 가장 단순하게 테스트 대상 클래스를 변경하여 숨겨진 오브젝트 속성(features)에 대한 가시성을 제공하는 오퍼레이션을 추가하는 방법이 있음. 하지만 테스트 대상 클래스에 변경이 가해짐에 따라(intrusive) 오버헤드가 증가할 수 있고 테스트 코드가 추가된 클래스가 원래의 클래스가 의도한 대로 동일하게 동작한다는 보장을 할 수 없으므로 그다지 바람직하지 않은 방법임
  • 또 다른 해결책은 테스트 대상 클래스의 서브 클래스를 생성하고 여기에 테스트를 위한 추가 오퍼레이션을 정의하여 이 서브 클래스를 테스트 하는 방법. 테스트 대상 클래스의 직접적인 변경 없이(non-intrusive) 테스트가 가능하지만 서브 클래스가 상속된 특성(inherited properties)들의 상태에 대한 완전한 가시성을 가지지 못하는 경우 무용지물(, C++ 클래스의 private 속성은 서브 클래스에서도 가시적이지 않음)
  • 위의 두 가지 방법 외에도 여러 객체 지향 언어들은 Encapsulation을 깨고 클래스 속성에 대한 가시성을 얻기 위한 나름의 메커니즘을 지원함(, C++ friend 서브프로그램, 물리적 오브젝트 구조에 접근하는 하위 오퍼레이션을 제공하는 Smalltalkinspectors )
  • Non-instantiatable 클래스 테스팅 문제: 충분한 정보를 포함하고 있지 않아서 인스턴스가 생성될 수 없는 Non-instantiatable 클래스에는 abstract 클래스, generic(parameterized) 클래스, mixin 클래스 등이 있다. 인스턴스를 생성할 수 없으므로 이 클래스들은 있는 그대로 테스트가 불가능하며, 테스터는 정보 부족으로 인한 향후 여러 다른 바인딩 가능성을 고려한 최소한의 테스트 케이스 집합을 개발해야만 함(아직 더 연구가 필요한 주제임)


상속(Inheritance)

  • 재테스팅(retesting) 문제: 서브 클래스가 하나 또는 여러 개의 베이스 클래스의 특성(properties)을 상속받는 경우 이미 테스트가 완료된 베이스 클래스의 특성을 추가적인 테스팅 없이 재사용 가능하다고 보는 직관과 달리 부모 클래스로부터 상속받은 특성의 일부는 서브 클래스의 문맥(context)에서 재테스팅이 필요함
  • 상속된 특성 전부를 하위 클래스에서 매번 재테스팅 하는 것을 피하기 위해 그 중 어떤 것이 하위 클래스의 문맥에서 재테스트가 필요한지 진단이 요구되며, 객체 지향 언어마다 지원하는 상속 메커니즘(, Strict inheritance, Subtyping, Subclassing, Multiple inheritance )과 구현 방법이 다르므로 모든 언어에 적용되는 절대적인 규칙은 없음
  • 서브 클래스에서 상속된 오퍼레이션을 새로운 구현으로 덮어쓰는 overriding의 경우 덮어써진 오퍼레이션(the overridden operation)의 재테스팅이 당연히 필요하며 더불어 상속 트리 상에 위치한 여러 다른 클래스의 오퍼레이션들 중에서 해당 overridden 오퍼레이션을 구현에 참조한 것들도 모두 재테스트가 필요할 수 있다.


다형성(Polymorphism)

  • 하나의 이름(, 변수명, 참조명)이 상속 트리 상에 존재하는 여러 다른 클래스의 인스턴스를 받을 수 있는 다형성 환경에서는 테스트에서 쉽게 발견이 어려운 잘못된 캐스팅(타입 전환 에러)’이 발생할 가능성이 높음
  • 동적 바인딩의 불확정성(Undecidability of dynamic binding) 문제: 다형성을 가진 이름이 여러 다른 클래스의 오브젝트를 받을 수 있으므로 특정 오퍼레이션을 호출 시 실제 런타임이 될 때가지는 실행되는 코드가 어떤 코드인지(원래의 구현이 불리어질지 하위 클래스에서 재정의된 구현이 불리어질지) 예측할 수 없음. 따라서 이러한 유형의 타입 에러는 컴파일 시에는 대개 발견하지 못함
  • 상속 트리의 확장성(Extensibility of hierarchies): 다형성을 가진 하나 또는 여러 개의 패러미터(polymorphic parameters)가 있는 오퍼레이션의 테스팅에서도 위와 유사한 문제가 발생하며, 더욱이 클래스 상속 트리(a hierarchy of classes)는 자유롭게 확장 가능하므로 모든 가능한 바인딩에 대한 테스트 계획이 불가능해진다.


결론

객체 지향 소프트웨어의 테스팅을 하는데 있어서 전통적인 절차 중심 소프트웨어를 위해 개발된 기존 기법에 일부 의존이 가능하지만 아키텍쳐의 차이에서 생기는 상이점들과 객체 지향 패러다임 고유의 특성들은 객체 지향에 특화된 테스트 방법의 개발이 중요함을 보여준다.


반응형