2012-01-25 29 views
20

में 'पैरामीटरकृत' परीक्षण लिख सकता हूं, मैं डेल्फी लाइब्रेरी का परीक्षण करने के लिए डुनिट का उपयोग कर रहा हूं। मैं कभी-कभी मामलों में चलाता हूं, जहां मैं फ़ंक्शन में एकाधिक इनपुट जांचने के लिए कई समान परीक्षण लिखता हूं।क्या मैं डुनिट

क्या डुनिट में पैरामीटर परीक्षण के बारे में लिखने का कोई तरीका है (कुछ जैसा दिखता है)? उदाहरण के लिए एक इनपुट और अपेक्षित आउटपुट को उपयुक्त परीक्षण प्रक्रिया में निर्दिष्ट करने के लिए, फिर टेस्ट सूट चलाएं और परीक्षण के एकाधिक रनों में से कौन सा प्रतिक्रिया विफल हो रही है?

(संपादित करें: एक उदाहरण)

उदाहरण के लिए, मैं इस तरह दो परीक्षणों था:

procedure TestMyCode_WithInput2_Returns4(); 
var 
    Sut: TMyClass; 
    Result: Integer; 
begin 
    // Arrange: 
    Sut := TMyClass.Create; 

    // Act: 
    Result := sut.DoStuff(2); 

    // Assert 
    CheckEquals(4, Result); 
end; 

procedure TestMyCode_WithInput3_Returns9(); 
var 
    Sut: TMyClass; 
    Result: Integer; 
begin 
    // Arrange: 
    Sut := TMyClass.Create; 

    // Act: 
    Result := sut.DoStuff(3); 

    // Assert 
    CheckEquals(9, Result); 
end; 

मैं इन परीक्षणों कि लेकिन साथ बिल्कुल वही बात कर के और भी अधिक हो सकता है विभिन्न इनपुट और उम्मीदें। मैं उन्हें एक परीक्षण में विलय नहीं करना चाहता, क्योंकि मैं उन्हें स्वतंत्र रूप से पास या विफल होने में सक्षम होना चाहता हूं।

+1

क्या आपका मतलब सूची में सभी इनपुट मानों के लिए परीक्षण मामलों की गतिशील रचना है? मेरा (छोटा) [ओपनसीटीएफ] (http://sourceforge.net/projects/openctf/) परीक्षण ढांचे में परीक्षण मामलों के गतिशील निर्माण के लिए कोड शामिल है। यह डुनिट पर आधारित है। – mjn

+0

आप हमेशा टेस्टक्लास में एक सामान्य पैरामीटरयुक्त विधि लिख सकते हैं और एक या अधिक विशिष्ट (प्रकाशित) परीक्षण विधियों से कॉल कर सकते हैं। टेस्टकेस की चेक (नहीं) बराबर विधि (कोड) कोड कोड को संक्षिप्त रखने में मदद करने के लिए यहां भी मदद कर सकती है और फिर भी प्रत्येक परीक्षण के लिए एक विशिष्ट विफलता संदेश प्रदान करती है। –

+0

@ मार्जन परीक्षण विधि निष्पादन को रोक देगा जैसे ही पहली जांच (नहीं) बराबर विफल हो जाती है - परीक्षण मामलों की गतिशील रचना इस समस्या को हल करती है, अन्य सभी मानों का अभी भी परीक्षण किया जाएगा – mjn

उत्तर

3

क्या यह पर्याप्त होगा यदि डुनिट ने इस तरह कोड लिखने की अनुमति दी, जहां AddTestForDoStuff की प्रत्येक कॉल आपके उदाहरण के समान परीक्षण केस तैयार करेगी?

Suite.AddTestForDoStuff.With(2).Expect(4); 
Suite.AddTestForDoStuff.With(3).Expect(9); 

मैं एक उदाहरण है कि कैसे इस आज बाद में किया जा सकता है पोस्ट करने के लिए कोशिश करता हूँ ...


नेट के लिए वहाँ पहले से ही कुछ इसी तरह है: Fluent दावे

http://www.codeproject.com/Articles/784791/Introduction-to-Unit-Testing-with-MS-tests-NUnit-a

+0

उन लाइनों के साथ कुछ अच्छा होगा। लेकिन शायद इसे विशिष्ट परीक्षण को तर्क के रूप में भी लेने की आवश्यकता होगी, है ना? जैसे 'सुइट.एडटेस्ट (' डूस्टफ ')। अराजकता (2)। (4) ' –

+0

@ माथीस फाल्कनबर्ग: या कम से कम एक संदेश जोड़ने की संभावना। –

+0

यह उत्तर काफी अच्छा है। यह पहली बार है जब मैंने कभी भी इच्छापूर्ण सोच के लिए एक अप-वोट दिया है। हाँ, अगर आप ऐसा कर सकते हैं तो यह बहुत अच्छा नहीं होगा !! वैसे भी, +1 डाउन-पेमेंट अर्जित किया जाएगा यदि आप वास्तव में कोड का उत्पादन कर सकते हैं। –

0

यहां आपके TTestCase वंशजों (वास्तविक) परीक्षण विधियों (:

से बुलाए गए सामान्य पैरामीटरयुक्त परीक्षण विधि का उपयोग करने का एक उदाहरण है)
procedure TTester.CreatedWithoutDisplayFactorAndDisplayString; 
begin 
    MySource := TMyClass.Create(cfSum); 

    SendAndReceive; 
    CheckDestinationAgainstSource; 
end; 

procedure TTester.CreatedWithDisplayFactorWithoutDisplayString; 
begin 
    MySource := TMyClass.Create(cfSubtract, 10); 

    SendAndReceive; 
    CheckDestinationAgainstSource; 
end; 

हाँ, वहाँ कुछ दोहराव है, लेकिन कोड का मुख्य दोहराव एक पूर्वज कक्षा में SendAndReceive और CheckDestinationAgainstSource तरीकों में इन तरीकों में से बाहर ले जाया गया था:

procedure TCustomTester.SendAndReceive; 
begin 
    MySourceBroker.CalculationObject := MySource; 
    MySourceBroker.SendToProtocol(MyProtocol); 
    Check(MyStream.Size > 0, 'Stream does not contain xml data'); 
    MyStream.Position := 0; 
    MyDestinationBroker.CalculationObject := MyDestination; 
    MyDestinationBroker.ReceiveFromProtocol(MyProtocol); 
end; 

procedure TCustomTester.CheckDestinationAgainstSource(const aCodedFunction: string = ''); 
var 
    ok: Boolean; 
    msg: string; 
begin 
    if aCodedFunction = '' then 
    msg := 'Calculation does not match: ' 
    else 
    msg := 'Calculation does not match. Testing CodedFunction ' + aCodedFunction + ': '; 

    ok := MyDestination.IsEqual(MySource, MyErrors); 
    Check(Ok, msg + MyErrors.Text); 
end; 

CheckDestinationAgainstSource में पैरामीटर को भी अनुमति देता है उपयोग की इस प्रकार के लिए:

procedure TAllTester.AllFunctions; 
var 
    CF: TCodedFunction; 
begin 
    for CF := Low(TCodedFunction) to High(TCodedFunction) do 
    begin 
    TearDown; 
    SetUp; 
    MySource := TMyClass.Create(CF); 
    SendAndReceive; 
    CheckDestinationAgainstSource(ConfiguredFunctionToString(CF)); 
    end; 
end; 

यह पिछले परीक्षण भी TRepeatedTest वर्ग का उपयोग कर कोडित किया जा सकता है, लेकिन मैं उस वर्ग का उपयोग करने के बजाय unintuitive पाते हैं। उपर्युक्त कोड मुझे कोडिंग कोड और समझदार विफलता संदेशों का उत्पादन करने में अधिक लचीलापन देता है। हालांकि पहली विफलता पर परीक्षण को रोकने की कमी है।

11

मुझे लगता है कि आप कुछ इस तरह की तलाश में हैं:

unit TestCases; 

interface 

uses 
    SysUtils, TestFramework, TestExtensions; 

implementation 

type 
    TArithmeticTest = class(TTestCase) 
    private 
    FOp1, FOp2, FSum: Integer; 
    constructor Create(const MethodName: string; Op1, Op2, Sum: Integer); 
    public 
    class function CreateTest(Op1, Op2, Sum: Integer): ITestSuite; 
    published 
    procedure TestAddition; 
    procedure TestSubtraction; 
    end; 

{ TArithmeticTest } 

class function TArithmeticTest.CreateTest(Op1, Op2, Sum: Integer): ITestSuite; 
var 
    i: Integer; 
    Test: TArithmeticTest; 
    MethodEnumerator: TMethodEnumerator; 
    MethodName: string; 
begin 
    Result := TTestSuite.Create(Format('%d + %d = %d', [Op1, Op2, Sum])); 
    MethodEnumerator := TMethodEnumerator.Create(Self); 
    Try 
    for i := 0 to MethodEnumerator.MethodCount-1 do begin 
     MethodName := MethodEnumerator.NameOfMethod[i]; 
     Test := TArithmeticTest.Create(MethodName, Op1, Op2, Sum); 
     Result.addTest(Test as ITest); 
    end; 
    Finally 
    MethodEnumerator.Free; 
    End; 
end; 

constructor TArithmeticTest.Create(const MethodName: string; Op1, Op2, Sum: Integer); 
begin 
    inherited Create(MethodName); 
    FOp1 := Op1; 
    FOp2 := Op2; 
    FSum := Sum; 
end; 

procedure TArithmeticTest.TestAddition; 
begin 
    CheckEquals(FOp1+FOp2, FSum); 
    CheckEquals(FOp2+FOp1, FSum); 
end; 

procedure TArithmeticTest.TestSubtraction; 
begin 
    CheckEquals(FSum-FOp1, FOp2); 
    CheckEquals(FSum-FOp2, FOp1); 
end; 

function UnitTests: ITestSuite; 
begin 
    Result := TTestSuite.Create('Addition/subtraction tests'); 
    Result.AddTest(TArithmeticTest.CreateTest(1, 2, 3)); 
    Result.AddTest(TArithmeticTest.CreateTest(6, 9, 15)); 
    Result.AddTest(TArithmeticTest.CreateTest(-3, 12, 9)); 
    Result.AddTest(TArithmeticTest.CreateTest(4, -9, -5)); 
end; 

initialization 
    RegisterTest('My Test cases', UnitTests); 

end. 

जो जीयूआई परीक्षण धावक में इस तरह दिखता है:

enter image description here

मैं बहुत अगर पता करने के लिए दिलचस्पी होगी मैं इस बारे में एक उप-इष्टतम तरीके से चले गए हैं। डुनिट इतना अविश्वसनीय रूप से सामान्य और लचीला है कि जब भी मैं इसका उपयोग करता हूं, मैं हमेशा महसूस करता हूं कि समस्या को हल करने के लिए मैंने एक बेहतर, सरल तरीका याद किया है।

+2

मुझे वैसे ही लगता है ... यही कारण है कि मैंने यह प्रश्न पोस्ट किया। जबकि आपका कोड निश्चित रूप से वांछित आउटपुट उत्पन्न करता है, मैं अपने परीक्षणों को और अधिक पठनीय बनाना चाहता हूं। 'CreateTest' विधि परीक्षण कोड में जटिलता की एक परत पेश करती है जिसे मैं वास्तव में टालना चाहता हूं ... –

19

आप अपने डुनिट परीक्षणों को बेहतर बनाने के लिए डीएसएचआरपी का उपयोग कर सकते हैं। विशेष रूप से नई इकाई DSharp.Testing.DUnit.pas (डेल्फी 2010 और उच्चतर में)।

टेस्टफ्रेमवर्क के बाद बस इसे अपने उपयोग में जोड़ें और आप अपने परीक्षण मामले में विशेषताओं को जोड़ सकते हैं। तो यह ऐसा दिखाई दे सकता:

unit MyClassTests; 

interface 

uses 
    MyClass, 
    TestFramework, 
    DSharp.Testing.DUnit; 

type 
    TMyClassTest = class(TTestCase) 
    private 
    FSut: TMyClass; 
    protected 
    procedure SetUp; override; 
    procedure TearDown; override; 
    published 
    [TestCase('2;4')] 
    [TestCase('3;9')] 
    procedure TestDoStuff(Input, Output: Integer); 
    end; 

implementation 

procedure TMyClassTest.SetUp; 
begin 
    inherited; 
    FSut := TMyClass.Create; 
end; 

procedure TMyClassTest.TearDown; 
begin 
    inherited; 
    FSut.Free; 
end; 

procedure TMyClassTest.TestDoStuff(Input, Output: Integer); 
begin 
    CheckEquals(Output, FSut.DoStuff(Input)); 
end; 

initialization 
    RegisterTest(TMyClassTest.Suite); 

end. 

जब आप चलाने के लिए यह अपने परीक्षण इस तरह दिखता है:

enter image description here

के बाद से डेल्फी में गुण सिर्फ स्थिरांक स्वीकार गुण सिर्फ एक स्ट्रिंग जहां के रूप में तर्क ले मूल्य अर्धविराम से अलग होते हैं। लेकिन कुछ भी आपको अपनी खुद की विशेषता वर्ग बनाने से रोकता है जो "जादू" तारों को रोकने के लिए सही प्रकार के कई तर्क लेते हैं। वैसे भी आप उन प्रकार तक सीमित हैं जो स्थिर हो सकते हैं।

आप विधि के प्रत्येक तर्क पर मान गुण भी निर्दिष्ट कर सकते हैं और इसे किसी भी संभावित संयोजन (जैसे NUnit) के साथ बुलाया जाता है।

व्यक्तिगत उत्तरों का जिक्र करते हुए व्यक्तिगत रूप से मैं यूनिट परीक्षण लिखते समय जितना संभव हो उतना छोटा कोड लिखना चाहता हूं। मैं यह भी देखना चाहता हूं कि जब मैं कार्यान्वयन भाग के माध्यम से खोदने के बिना इंटरफेस भाग को देखता हूं तो परीक्षण क्या होता है (मैं यह नहीं कहूंगा: "BDD करें")। यही कारण है कि मैं घोषणात्मक तरीका पसंद करता हूं।

+0

+1 यह वास्तव में बहुत उपयोगी और दिलचस्प लग रहा है।उसे हमारे ध्यान में लाने के लिए धन्यवाद। –

+0

+1 वास्तव में! मैं उसमें देखना सुनिश्चित करूँगा। मुझे लगता है कि अब तक यह सबसे परेशानी मुक्त दृष्टिकोण है! –