2009-04-01 6 views
10

DUnit의 작동 방식은 게시 된 메서드를 작성하는 것이며 DUnit은이를 테스트로 실행합니다. 내가하고 싶은 것은 조금 다릅니다. 데이터를 기반으로 런타임에 테스트를 만들고 싶습니다. 출력 파일을 만들기 위해 입력 파일을 처리하는 특정 모듈을 테스트하려고합니다. 나는 잘 알려진 출력 파일을 가지고 테스트 입력 파일들을 가지고있다. 아이디어는 각 입력 파일에 대해 하나씩 입력을 처리하고 알려진 양호한 출력에 대해 출력을 검사하는 테스트를 동적으로 작성하는 것입니다.데이터 기반 DUnit 테스트

여기서 실제 데이터 원본은 중요하지 않습니다. 어려움은 DUnit을 데이터 기반 방식으로 작동하게 만드는 것입니다. 이 문제를 위해 데이터 소스가 단지 난수 생성기 였다고 가정합니다. 각각

  1. 런타임에 이름

    런타임에 몇 가지 테스트 오브젝트 (TTestCase 또는 무엇이든)을 작성, 그 중 10 말 : 여기 어려움의 마음에 도달 예를 들어 구체적인 문제는 무작위로 생성 된 정수에서. ('name'은 테스트 러너 트리에 나타나는 테스트 이름을 의미합니다.)

  2. 임의의 정수를 기준으로 합격하거나 합격하지 못합니다. 짝수 패스, 홀수 패스 실패. 이 같은 일을 가능하게하기 염두에 충분한 유연성 설계되었습니다처럼은 DUnit의 디자인에서

, 그것은 보인다. 나는 그것이 그것이라고 확신하지 않는다. 나는 TAbstractTest와 ITest를 상속 받아 자신 만의 테스트 클래스를 만들려고했지만 몇 가지 중요한 메소드에 접근 할 수 없었다. 또한 TTestCase에서 상속을 시도했지만 그 클래스는 게시 된 메서드를 실행하는 아이디어와 밀접하게 연결되어 있습니다. 테스트는 메서드의 이름을 따서 명명되었으므로 '호출'이라는 단일 개체를 사용할 수 없습니다. 모든 나의 테스트는 'go'라고 불릴 것이고 나는 모든 테스트가 개별적으로 명명되기를 원한다.)

아니면 DUnit 대신 내가 원하는 것을 할 수있는 대안이 있습니까?

답변

17
program UnitTest1; 

{$IFDEF CONSOLE_TESTRUNNER} 
{$APPTYPE CONSOLE} 
{$ENDIF} 

uses 
    Forms, Classes, SysUtils, 
    TestFramework, 
    GUITestRunner, 
    TextTestRunner; 

{$R *.RES} 

type 
    TIntTestCase = class(TTestCase) 
    private 
    FValue: Integer; 
    public 
    constructor Create(AValue: Integer); reintroduce; 
    function GetName: string; override; 
    published 
    procedure Run; 
    end; 

{ TIntTestCase } 

constructor TIntTestCase.Create(AValue: Integer); 
begin 
    inherited Create('Run'); 
    FValue := AValue; 
end; 

function TIntTestCase.GetName: string; 
begin 
    Result := Format('Run_%.3d', [FValue]); 
end; 

procedure TIntTestCase.Run; 
begin 
    Check(FValue mod 2 = 0, Format('%d is not an even value', [FValue])); 
end; 

procedure RegisterTests; 
const 
    TestCount = 10; 
    ValueHigh = 1000; 
var 
    I: Integer; 
begin 
    Randomize; 
    for I := 0 to TestCount - 1 do 
    RegisterTest(TIntTestCase.Create(Random(ValueHigh) + 1)); 
end; 

begin 
    Application.Initialize; 
    RegisterTests; 
    if IsConsole then 
    TextTestRunner.RunRegisteredTests 
    else 
    GUITestRunner.RunRegisteredTests; 
end. 
+0

정말 대단합니다. 감사. 나는 비슷한 것을 시도하고 있었지만 길을 따라 약간의 실수가 가해져서 작동하지 않게되었습니다. 다시 한번 감사드립니다. –

+0

도와 드릴 수있어서 다행입니다. –

+0

동일한 테스트 클래스에서 데이터 기반 사례와 일반 사례 모두를 제안 하시겠습니까? –

2

기본적으로 다른 테스트를 호출하는 단일 "수퍼 테스트"기능이 필요하다고 말하고 싶지만, 각 데이터 파일마다 하나씩 테스트합니다. 이것이 DUnit 테스트 중 하나입니다. 루프에서 사용 가능한 각 파일을 순차적으로로드하고 적절한 경우 Check를 사용하여 테스트를 실행합니다.

동일한 프로젝트에서 최종 앱 및 데이터로드 및 분석을 테스트하는 데 사용하는 다른 방법은 FinalBuilder와 같은 응용 프로그램을 루프하는 것입니다 (아마 DUnit 앱에서도 반복 할 수있을 것입니다). 매개 변수)를 다양한 데이터 파일로 대체합니다. 그러면 앱이 실행되고 분석이 끝난 다음 저장 후 종료됩니다. 그런 다음 다른 앱에서 결과 데이터를 이상적인 데이터와 비교하고 필요한 경우 실패를보고합니다.

+0

각 테스트가 실행되는 데 시간이 걸리며 일반적으로 한 번에 몇 개만 중단되므로 수퍼 테스트 아이디어가 작동하지 않습니다. 코드를 수정하기 위해 테스트 만 격리 할 수 ​​있어야합니다. 매번 전체 수퍼 테스트를 실행하는 것은 불행히도 매우 느립니다. –

+0

@dangph : 이것이 FinalBuilder 테스트가 시작되는 곳입니다. 개별 테스트는 실패 할 수 있지만, try/catch 기능을 사용하여 단일 실패를 기록하지만 나머지는 계속 수행 한 후 마지막 빌드 직후 빌드를 실패합니다 . – mj2008

+0

@ mj2008, 우리가하고있는 일은 다소 거칠게 모듈을 리팩터링하는 것입니다. 우리가 물건을 리팩토링하면 테스트가 중단됩니다. 테스트가 매우 느리기 때문에 DUnit 체크 박스를 사용하여 깨진 테스트를 실행하기를 원하며 일반적으로 몇 번 반복해야합니다. –