개념: 스텁
이 가이드라인은 지속적인 개발이 필요한 컴포넌트의 플레이스홀더로 사용될 수 있는 스텁의 작성 방법을 설명합니다. 스텁은 자신과 자신이 허용하는 매개변수를 선언하는 작업 외에는 실제로 아무 작업도 수행하지 않는 컴포넌트입니다. 스텁에는 컴파일하고 나머지 컴포넌트와 연결할 수 있는 충분한 코드가 포함되어 있습니다.
관계
관련 요소
기본 설명

소개

컴포넌트의 인터페이스에 입력을 보내고 컴포넌트의 입력 처리를 기다린 후 결과를 확인하는 방식으로 컴포넌트를 테스트합니다. 처리 과정에서 컴포넌트는 다른 컴포넌트에 입력을 보내고 그 결과를 사용하는 방식으로 다른 컴포넌트를 사용할 수 있습니다.

함께 표시된 텍스트에서 설명되는 다이어그램

그림 1: 구현된 컴포넌트 테스트

다른 컴포넌트는 테스트에 다음과 같은 문제를 일으킬 수 있습니다.

  1. 아직 구현되지 않은 컴포넌트.
  2. 테스트가 작동하지 못하도록 하거나 사용자의 컴포넌트 때문에 테스트가 실패한 것이 아님을 발견하는 데 오랜 시간이 걸리게 하는 결함이 있을 수 있습니다.
  3. 필요한 경우 테스트 실행을 어렵게 만들 수 있습니다. 컴포넌트가 상용 데이터베이스인 경우 회사에는 모든 사람을 위한 충분한 유동 라이센스가 없을 수 있습니다. 또는 컴포넌트 중 하나가 별도의 실험실에서 계획된 시간에만 사용 가능한 하드웨어일 수 있습니다.
  4. 테스트가 너무 느려서 충분히 실행되지 않도록 만들 수 있습니다. 예를 들어 데이터베이스 초기화는 테스트당 5분이 걸릴 수 있습니다.
  5. 컴포넌트가 특정 결과를 생성하도록 하기가 어려울 수 있습니다. 예를 들어 디스크에 쓰는 메소드의 각각이 "디스크 공간 없음" 오류를 처리하도록 할 수 있습니다. 해당 메소드를 호출하는 순간에 디스크 공간이 없는지 어떻게 확인합니까?

이러한 문제를 방지하려면 스텁 컴포넌트(가상 오브젝트(mock objects)라고도 함)를 사용하도록 선택할 수 있습니다. 스텁 컴포넌트는 실제 컴포넌트처럼 동작하며, 적어도 사용자 컴포넌트가 테스트에 응답하는 동안 스텁 컴포넌트에 보낸 값에 대해 실제 컴포넌트처럼 동작합니다. 스텁 컴포넌트는 거의 모든 컴포넌트의 동작을 그대로 흉내내려고 하는 범용 에뮬레이터 이상의 동작을 수행합니다. 예를 들어 하드웨어에 대한 소프트웨어 에뮬레이터를 빌드하는 것은 좋은 전략입니다. 소프트웨어 에뮬레이터는 느리지만 하드웨어처럼 동작합니다. 이 에뮬레이터는 보다 향상된 디버깅을 지원하고 복사본을 더 많이 사용할 수 있고 하드웨어가 완료되기 전에 사용할 수 있기 때문에 유용합니다.

함께 표시된 텍스트에서 설명되는 다이어그램

그림 2: 의존하는 컴포넌트를 제외함으로써 구현된 컴포넌트를 테스트

스텁에는 두 가지 단점이 있습니다.

  1. 빌드에 비용이 많이 들 수 있습니다. (특히 에뮬레이터가 이에 해당합니다.) 소프트웨어 자체가 되므로 유지보수되어야 합니다.
  2. 오류를 감출 수 있습니다. 예를 들어 사용자 컴포넌트는 삼각 함수를 사용하지만 아직 사용 가능한 라이브러리가 없다고 가정해 보십시오. 세 가지 테스트 케이스가 세 각, 10도, 45도, 90도의 sine 값을 요청합니다. 계산기를 사용하여 올바른 값을 찾은 후 각각 0.173648178, 0.707106781, 1.0을 리턴하는 sine의 스텁을 구성합니다. sine 함수가 라디안의 인수를 사용하여 -0.544021111, 0.850903525, 0.893996664를 리턴하는 실제 삼각 함수 라이브러리와 사용자 컴포넌트를 통합할 때까지 모든 기능이 제대로 작동됩니다. 이 문제는 나중에 발견되는 코드의 결함이며 생각보다 많은 노력이 필요합니다.

스텁 및 소프트웨어 디자인 사례

아직 실제 컴포넌트를 사용할 수 없기 때문에 스텁이 구성되지 않은 경우에는 배치 다음에 스텁을 유지하도록 해야 합니다. 스텁이 지원하는 테스트는 제품 유지보수 과정에서 중요할 수 있습니다. 따라서 스텁은 일회용 코드보다 높은 표준으로 작성되어야 합니다. 스텁은 제품 코드의 표준을 충족할 필요는 없지만(예: 대부분의 경우 자체 테스트 세트가 필요 없음), 나중에 개발자가 스텁을 제품 변경의 컴포넌트로 유지보수해야 합니다. 해당 유지보수가 너무 엄격하면 스텁은 삭제되고 스텁에 대한 투자가 손실됩니다.

특히 스텁이 유지되면 스텁은 컴포넌트 디자인을 변경합니다. 예를 들어 사용자 컴포넌트가 데이터베이스를 사용하여 키/값 쌍을 지속적으로 저장한다고 가정해 보십시오. 두 가지 디자인 시나리오를 고려하십시오.

시나리오 1: 테스트 및 일반 사용에 데이터베이스가 사용됩니다. 데이터베이스의 존재는 컴포넌트로부터 숨겨지면 안됩니다. 데이터베이스 이름으로 이를 초기화할 수 있습니다.

 public Component(

String databaseURL) { try {     databaseConnection =         DriverManager.getConnection(databaseURL);     ... } catch (SQLException e) {...}     }

그리고 값을 읽거나 쓴 각 위치에서 SQL 문을 구성하지 않아도 확실히 SQL을 포함하는 일부 메소드가 있습니다. 예를 들어 값을 필요로 하는 컴포넌트 코드는 이 컴포넌트 메소드를 호출합니다.
 public String get(String key) { try {     Statement stmt =       databaseConnection.createStatement();     ResultSet rs = stmt.executeQuery(       

"SELECT value FROM Table1 WHERE key=" + key);     ... } catch (SQLException e) {...}     }

시나리오 2: 테스트를 위해 데이터베이스는 스텁으로 교체됩니다. 컴포넌트 코드는 실제 데이터베이스 또는 스텁에 대해 실행되는지 여부에 관계없이 동일해 보여야 합니다. 따라서 컴포넌트 코드를 코딩하여 추상 인터페이스의 메소드를 사용해야 합니다.
 

interface KeyValuePairs { String 

get(String key); void 

put(String key, String value);     }

테스트는 해시 테이블과 같은 단순한 오브젝트와 함께 KeyValuePairs를 구현합니다.
 

class FakeDatabase implements KeyValuePairs  { Hashtable table = new Hashtable(); public String 

get(String key) {     return (String) table.get(key); } public void 

put(String key, String value) {     table.put(key, value); }     }
테스트에서 사용되지 않는 컴포넌트는 KeyValuePairs 인터페이스에 대한 호출을 다음 SQL 문으로 변환한 어댑터 오브젝트를 사용합니다.
 

class DatabaseAdapter implements KeyValuePairs { private Connection databaseConnection; public DatabaseAdapter(String databaseURL) {     try {         databaseConnection =             DriverManager.getConnection(databaseURL);         ...     } catch (SQLException e) {...} } public String 

get(String key) {     try {         Statement stmt =            databaseConnection.createStatement();         ResultSet rs = stmt.executeQuery(           "SELECT value FROM Table1 WHERE key=" + key);         ...     } catch (SQLException e) {...} } public void 

put(String key, String value) {     ... }     }

사용자 컴포넌트는 테스트 및 기타 클라이언트에 대해 모두 하나의 생성자를 가질 수 있습니다. 해당 생성자는 KeyValuePairs를 구현하는 오브젝트를 취합니다. 또는 생성자는 컴포넌트의 일반 클라이언트를 데이터베이스 이름으로 전달해야 하는 테스트에만 해당 인터페이스를 제공할 수 있습니다.
 class Component { 

public Component(String databaseURL) {     this.valueStash = new DatabaseAdapter(databaseURL); } // For testing. 

protected Component(KeyValuePairs valueStash) {     this.valueStash = valueStash; }     }

따라서 클라이언트 프로그래머의 관점에서 두 가지 디자인 시나리오는 동일한 API를 생성하지만 한 API가 보다 즉시 테스트 가능합니다. (일부 테스트는 실제 데이터베이스를 사용할 수도 있고 일부는 스텁 데이터베이스를 사용할 수도 있습니다.)

추가 정보


스텁에 대한 자세한 내용은 다음을 참조하십시오.