2011-12-04 18 views
7

द्वारा निर्यात न किए गए एनयूएनआईटी रन एफ # परीक्षण कैसे बना सकता हूं मैंने एफ # में एक बड़ा मॉड्यूल लिखा है जो एक मामूली इंटरफ़ेस होता है। मॉड्यूल में कोड की लगभग 1000 पंक्तियां, 50 यूनिट परीक्षण, और निर्यात केवल एक आसानी से समझा जाता है।मैं मॉड्यूल

एक छोटी एफएसआई फ़ाइल लिखना अगली प्राकृतिक बात है। इसमें नामस्थान प्रदूषण को रोकने सहित कई फायदे हैं, जो दस्तावेज़ीकरण के लिए एक स्पष्ट स्थान प्रदान करते हैं, यह सुनिश्चित करते हुए कि यदि कोई आंतरिक इंटरेस्ट का पुन: उपयोग करने का निर्णय लेता है तो उन्हें साफ-सफाई करने के लिए प्रोत्साहन मिलेगा, और इसमें कोई संदेह नहीं है। मुझे यकीन है कि मैं यहां गाना बजानेवालों के लिए प्रचार कर रहा हूं, लेकिन फिर भी महसूस किया कि यह समझने लायक है कि मुझे एफएसआई फाइल रखने में मदद क्यों मिलती है।

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

मुझे लगता है कि कोड को कहीं और स्थानांतरित करने के लिए एक छोटा कामकाज है, इसे एक छोटे से .fs फ़ाइल में आयात करें, और केवल एक फ़ंक्शन को अग्रेषित करें। कुछ भाग्य के साथ सभी सहमत होंगे कि बस विद्रोह कर रहा है। क्या कोई बेहतर तरीका है?

संपादित करें: प्रतिक्रिया देने वाले सभी लोगों के लिए बहुत धन्यवाद। मैंने दोनों उत्तरों को उखाड़ फेंक दिया। मुझे बक्षीस को विभाजित करना पसंद होता, हालांकि यह संभव नहीं लगता है कि मैं (कुछ हद तक मनमाने ढंग से) टॉमस के जवाब को स्वीकार करूंगा।

+1

आप उपयोग करने की कोशिश है InternalsVisibleTo? http://devlicio.us/blogs/derik_whittaker/archive/2007/04/09/internalsvisibleto-testing-internal-methods-in-net-2-0.aspx –

+0

मुझे लगता है कि मेरी समस्या थोड़ा अलग है। मैं पहले से ही परीक्षणों को संकलित कर सकता हूं। हालांकि, अगर मैं उन्हें मॉड्यूल से निर्यात नहीं करता हूं तो मैं उन्हें नहीं चला सकता। मुझे लगता है कि विशेषता मदद करेगी अगर मैं यह कह सकूं कि सभी आंतरिक एनयूनीट तक पहुंच योग्य हैं। लेकिन मुझे नहीं पता कि इसके साथ कहां से शुरुआत करें, और न ही - ऐसा लगता है - Google करता है। परीक्षणों को एक अलग फ़ाइल में ले जाने के बाद मैं स्पष्ट रूप से विशेषता का उपयोग कर सकता हूं, लेकिन यह एक बुरी बुराई है। मैं उन्हें निष्पादन योग्य दस्तावेज के रूप में उपयोग करता हूं, इसलिए उन्हें वास्तव में कोड के करीब होना चाहिए। उत्तर के लिए बहुत - बहुत धन्यवाद। – user1002059

+0

यदि यह संकलित करता है, तो इसे चलाना चाहिए। जब तक आप एक ही असेंबली में वास्तविक कोड के रूप में परीक्षण नहीं करते हैं, या ऐसा कुछ नहीं। –

उत्तर

6

यदि आप अपने स्रोत में मॉड्यूल और कार्यों की दृश्यता निर्दिष्ट करने के लिए fsi फ़ाइल जोड़ रहे हैं, तो आपको सार्वजनिक रूप से सुलभ होने वाले सभी कार्यों की घोषणाएं शामिल करने की आवश्यकता होगी। इसका अर्थ यह है कि यदि NUnit को सार्वजनिक कार्यों के परीक्षण की आवश्यकता होती है, तो आपको उन्हें fsi फ़ाइल में शामिल करने की आवश्यकता होगी।

हालांकि, fsi फ़ाइल का उपयोग करने के बजाय F # में दृश्यता निर्दिष्ट करने का एक और तरीका भी है, आप अपनी घोषणाओं में उचित दृश्यता संशोधक जोड़ सकते हैं। इस तरह, आप सभी कार्यान्वयन विवरण और निर्यात केवल मुख्य कार्य और परीक्षण छिपा सकते हैं:

namespace MyLibrary 
open NUnit.Framework 

// Implementation details can be in this module 
// (which will not be visible outside of the library) 
module private Internal = 
    let foo n = n * 2 
    let bar n = n + 1 

// A public module can contain the public API (and use internal implementation)  
module public MyModule = 
    open Internal 
    let doWork n = foo (bar n) 

// To make the tests visible to NUnit, these can be placed in a public module 
// (but they can still access all functions from 'Internal') 
module public Tests = 
    open MyModule 

    [<Test>] 
    let ``does work for n = 1``() = 
    Assert.Equals(doWork 1, 4) 

fsi फ़ाइलों का उपयोग कर के साथ तुलना में, इस नुकसान यह है कि आप एक अलग फाइल कि अच्छी तरह से केवल का वर्णन नहीं है है आपके एपीआई के महत्वपूर्ण भागों। हालांकि, आपको जो चाहिए वह आपको मिलेगा - कार्यान्वयन विवरण छुपाएं और केवल एक ही समारोह और परीक्षण का पर्दाफाश करें।

+0

बहुत धन्यवाद उत्तर के लिए इस दृष्टिकोण का उपयोग होने पर परीक्षण और कोड को अंतःस्थापित करना संभव नहीं है? – user1002059

+0

@ user1002059 यह भी संभव होगा, लेकिन आपको 'निजी निजी foo() = ...' या 'public test_for_foo() = ...' का उपयोग करके प्रत्येक एकल फ़ंक्शन को चिह्नित करना होगा और सार्वजनिक रूप से सभी मॉड्यूल को शामिल करना होगा कुछ सार्वजनिक कार्यों। इसका अर्थ है अधिक टिप्पणियां, लेकिन यह एक संभावना है। –

2

दृष्टिकोण

आप प्रतिबिंब का उपयोग अपने निजी परीक्षण तरीकों को लागू करने का सहारा सकता है: आप एक ही सार्वजनिक NUnit परीक्षा पद्धति है जिसमें सभी निजी तरीकों से अधिक लूप होगा विधानसभा टेस्ट विशेषता के साथ उन लोगों लागू। इस दृष्टिकोण के लिए बड़ा नीचे की तरफ यह है कि आप एक समय में केवल एक असफल परीक्षण विधि देख सकते हैं (लेकिन हो सकता है कि आप इसे ठीक करने के लिए पैरामीटर परीक्षणों का उपयोग करने जैसे रचनात्मक कुछ देख सकें)।

उदाहरण

Program.fsi

namespace MyNs 

module Program = 
    val visibleMethod: int -> int 

Program.fs

namespace MyNs 

open NUnit.Framework 

module Program = 
    let implMethod1 x y = 
     x + y 

    [<Test>] 
    let testImpleMethod1() = 
     Assert.AreEqual(implMethod1 1 1, 2) 

    let implMethod2 x y z = 
     x + y + z 

    [<Test>] 
    let testImpleMethod2() = 
     Assert.AreEqual(implMethod2 1 1 1, 3) 

    let implMethod3 x y z r = 
     x + y + z + r 

    [<Test>] 
    let testImpleMethod3() = 
     Assert.AreEqual(implMethod3 1 1 1 1, -1) 

    let implMethod4 x y z r s = 
     x + y + z + r + s 

    [<Test>] 
    let testImpleMethod4() = 
     Assert.AreEqual(implMethod4 1 1 1 1 1, 5) 

    let visibleMethod x = 
     implMethod1 x x 
     + implMethod2 x x x 
     + implMethod3 x x x x 

TestProxy।FS (हमारे "दृष्टिकोण" के कार्यान्वयन)

module TestProxy 

open NUnit.Framework 

[<Test>] 
let run() = 
    ///we only want static (i.e. let bound functions of a module), 
    ///non-public methods (exclude any public methods, including this method, 
    ///since those will not be skipped by nunit) 
    let bindingFlags = System.Reflection.BindingFlags.Static ||| System.Reflection.BindingFlags.NonPublic 

    ///returns true if the given obj is of type TestAttribute, the attribute used for marking nunit test methods 
    let isTestAttr (attr:obj) = 
     match attr with 
     | :? NUnit.Framework.TestAttribute -> true 
     | _ -> false 

    let assm = System.Reflection.Assembly.GetExecutingAssembly() 
    let tys = assm.GetTypes() 
    let mutable count = 0 
    for ty in tys do 
     let methods = ty.GetMethods(bindingFlags) 
     for mi in methods do 
      let attrs = mi.GetCustomAttributes(false) 
      if attrs |> Array.exists isTestAttr then 
       //using stdout w/ flush instead of printf to ensure messages printed to screen in sequence 
       stdout.Write(sprintf "running test `%s`..." mi.Name) 
       stdout.Flush() 
       mi.Invoke(null,null) |> ignore 
       stdout.WriteLine("passed") 
       count <- count + 1 
    stdout.WriteLine(sprintf "All %i tests passed." count) 

उदाहरण आउटपुट (TestDriven.NET का प्रयोग करके)

सूचना हम कभी नहीं testImplMethod4 को पाने के बाद से यह testImpleMethod3 पर विफल रहता है:

running test `testImpleMethod1`...passed 
running test `testImpleMethod2`...passed 
running test `testImpleMethod3`...Test 'TestProxy.run' failed: System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation. 
    ----> NUnit.Framework.AssertionException : Expected: 4 
    But was: -1 
    at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) 
    at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
    at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) 
    C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\TestProxy.fs(29,0): at TestProxy.run() 
    --AssertionException 
    C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\Program.fs(25,0): at MyNs.Program.testImpleMethod3() 

0 passed, 1 failed, 4 skipped (see 'Task List'), took 0.41 seconds (NUnit 2.5.10). 
+0

बहुत धन्यवाद। यह एक दिलचस्प समाधान है जिसे मैंने नहीं सोचा है। हालांकि, मुझे आश्चर्य है कि क्या यह न्यूटिट को पुन: कार्यान्वित करने की दिशा में बहुत दूर जा रहा है। उदाहरण के लिए, यदि किसी भी परीक्षण में अपवाद की अपेक्षा की जाती है, तो [<कॉम्बिनेटोरियल>] या यहां तक ​​कि सेटअप और टियरडाउन जैसी चीजों का उपयोग किया जाता है, तो वहां बहुत काम करना होगा? एक बार फिर धन्यवाद। – user1002059

+0

आपका स्वागत है :) मैं मूल रूप से सिल्वरलाइट के तहत अपने xUnit.net/अनक्वोट परीक्षण चलाने के लिए इस समाधान के समान कुछ आया था: http://stackoverflow.com/questions/7053245/how-to-run-f -silverlight-पुस्तकालय-परियोजना-जोत-XUnit निवल योगदान आधारित इकाई-टी। मैं कल्पना कर सकता हूं कि सभी प्रकार के दावों का पूरी तरह से समर्थन करने से एनयूनीट बढ़ सकता है, यहां जटिलता का विस्तार किया जाएगा (आप वास्तव में एनयूनीट परीक्षण धावक पुनर्मूल्यांकन का एक प्रकार प्रदान कर रहे हैं), लेकिन यह मेरे लिए कोई समस्या नहीं थी क्योंकि अनक्वोट केवल 'एनयूनीट' का उपयोग करता है। Framework.AssertionException' और उस पर अपना स्वयं का दावा API प्रदान करता है। –

+0

एनयूनीट ओपन सोर्स के बाद से एक और विकल्प है जो आपके लिए कस्टम बिल्ड हैक करता है जो निजी परीक्षण विधियों को छोड़ने के लिए एनयूनीट के तर्क को हटा देता है (यह उन्हें देखता है, और उन्हें चला सकता है, लेकिन जो मूर्खतापूर्ण कारण जानबूझकर उनका समर्थन नहीं करता है) - या , जैसा कि मैंने आपके प्रश्न पर मेरी टिप्पणी में सुझाव दिया है, xUnit.net पर स्विच करें क्योंकि इसमें निजी परीक्षण विधियों को चलाने के बारे में कोई दिक्कत नहीं है, और रूपांतरण बहुत दर्द रहित होना चाहिए (बेशक, आपको कुछ आवश्यकता हो सकती है जो आपको NUnit से जोड़ती है) । –

संबंधित मुद्दे