2012-11-01 21 views
5

क्या Specs2 परीक्षण को डिज़ाइन करने के लिए कुछ पसंदीदा तरीका है, पिछले परीक्षणों के परिणामों पर निर्भर कई परीक्षणों के साथ?परस्पर निर्भर परीक्षणों के साथ एक स्पेक्स 2 डेटाबेस परीक्षण कैसे डिज़ाइन करता है?

नीचे, आपको मेरा वर्तमान परीक्षण सूट मिलेगा। मुझे टेस्ट टुकड़ों के बीच var एस पसंद नहीं है। हालांकि वे "आवश्यक" हैं, क्योंकि कुछ परीक्षण आईडी संख्या उत्पन्न करते हैं जो बाद के परीक्षणों का पुन: उपयोग करते हैं।

  1. मैं शायद बजाय एक Specs2 संदर्भ में आईडी नंबर की दुकान, या एक अलग वस्तु है कि सभी परिवर्तनशील राज्य रखती बनाना चाहिए? और विनिर्देश वस्तु में केवल परीक्षण टुकड़े रखें? या क्या कुछ और बेहतर दृष्टिकोण है?

  2. यदि कोई परीक्षण विफल रहता है, तो मैं उसी गहराई में शेष परीक्षण को रद्द करना चाहता हूं। क्या मैं परीक्षण टुकड़े एक-दूसरे पर निर्भर कर सकता हूं? (मैं जानता हूँ कि मैं (एक परीक्षण टुकड़ा में शेष matchers रद्द कर सकते हैं परिवर्तनशील परीक्षण का उपयोग करके, या के माध्यम से orSkip), लेकिन क्या बारे में पूरे टुकड़े रद्द?)

object DatabaseSpec extends Specification { 
    sequential 

    "The Data Access Object" should { 

    var someId = "" // These var:s feels error prone, is there a better way? 

    "save an object" >> { 
     someId = database.save(something) 
     someId must_!= "" 

     // I'd like to cancel the remaining tests, below, at this "depth", 
     // if this test fragmen fails. Can I do that? 
     // (That is, cancel "load one object", "list all objects", etc, below.) 
    } 

    "load one object" >> { 
     anObject = database.load(someId) 
     anObject.id must_== someId 
    } 

    "list all objects" >> { 
     objs = database.listAll() 
     objs.find(_.id == someId) must beSome 
    } 

    var anotherId = "" 
    ...more tests that create another object, and 
    ...use both `someId` and `anotherId`... 

    var aThirdId = "" 
    ...tests that use `someId`, `anotherId` and `aThirdId... 
    } 


    "The Data Access Object can also" >> { 
    ...more tests... 
    } 

} 
+0

बीटीडब्ल्यू specs2 3.x को इस सटीक समस्या को हल करने के लिए डिज़ाइन किया गया है, जहां आप पिछले परीक्षणों के परिणामों पर निर्भर मनमाने ढंग से परीक्षण कर सकते हैं। यहां देखें: https://etorreborre.github.io/specs2/guide/SPECS2-3.1.1/org.specs2.guide.CreateOnlineSpecifications.html – Eric

उत्तर

4

वहाँ अपने प्रश्न का 2 भागों हैं: मध्यस्थ राज्य भंडारण, और उदाहरण रोक जब एक विफल हो रहा है के लिए वार्स का उपयोग कर।

1 - का उपयोग करना वार्स

वहाँ

जब एक अस्थायी विनिर्देश का उपयोग कर वार्स का उपयोग कर के लिए कुछ विकल्प हैं।

आप अपने प्रक्रिया के चरणों का प्रतिनिधित्व lazy vals उपयोग कर सकते हैं:

object DatabaseSpec extends mutable.Specification { 
    sequential 

    "The Data Access Object" should { 

    lazy val id1 = database.save(Entity(1)) 
    lazy val loaded = database.load(id1) 
    lazy val list = database.list 

    "save an object" >> { id1 === 1 } 
    "load one object" >> { loaded.id === id1 } 
    "list all objects" >> { list === Seq(Entity(id1)) } 
    } 

    object database { 
    def save(e: Entity) = e.id 
    def load(id: Int) = Entity(id) 
    def list = Seq(Entity(1)) 
    } 
    case class Entity(id: Int) 
} 

के बाद से उन मूल्यों को आलसी हैं, वे केवल जब उदाहरण क्रियान्वित कर रहे हैं बुलाया जाएगा।

आप अपने वर्तमान विनिर्देश की संरचना आप भी एक उदाहरण में नवीनतम 1.12.3-स्नैपशॉट और समूह के सभी उन छोटे उम्मीदों उपयोग कर सकते हैं बदलने के लिए तैयार कर रहे हैं:

"The Data Access Object provides a save/load/list api to the database" >> { 

    lazy val id1 = database.save(Entity(1)) 
    lazy val loaded = database.load(id1) 
    lazy val list = database.list 

    "an object can be saved" ==> { id1 === 1 } 
    "an object can be loaded" ==> { loaded.id === id1 } 
    "the list of all objects can be retrieved" ==> { 
    list === Seq(Entity(id1)) 
    } 
} 

तो उन अपेक्षाओं के किसी भी तो असफल बाकी निष्पादित नहीं किया जाएगा और आप की तरह एक विफलता संदेश प्राप्त होगा:

x The Data Access Object provides a save/load/list api to the database 
    an object can not be saved because '1' is not equal to '2' (DatabaseSpec.scala:16) 

एक और संभावना है, जो 2 छोटे सुधार की आवश्यकता होगी, हो सकता है "फेंका" expectati का उपयोग कर विनिर्देशों लेखन के Given/When/Then रास्ता लेकिन उपयोग करने के लिए Given और When चरणों के अंदर ऑन्स। आप उपयोगकर्ता गाइड में देख सकते हैं, Given/When/Then चरणों तार से डेटा निकालने और अगले Given/When/Then के लिए लिखा था जानकारी पास:

import org.specs2._ 
import specification._ 
import matcher.ThrownExpectations 

class DatabaseSpec extends Specification with ThrownExpectations { def is = 
    "The Data Access Object should"^ 
    "save an object"   ^save^ 
    "load one object"   ^load^ 
    "list all objects"   ^list^ 
    end 

    val save: Given[Int] = groupAs(".*") and { (s: String) => 
    database.save(Entity(1)) === 1 
    1 
    } 

    val load: When[Int, Int] = groupAs(".*") and { (id: Int) => (s: String) => 
    val e = database.load(id) 
    e.id === 1 
    e.id 
    } 

    val list: Then[Int] = groupAs(".*") then { (id: Int) => (s: String) => 
    val es = database.list 
    es must have size(1) 
    es.head.id === id 
    } 
} 

सुधार है, जो मैं करने जा रहा हूं, कर रहे हैं:

  • असफलताओं के रूप में रिपोर्ट करने के लिए विफलता अपवाद अपवाद और त्रुटियों
  • स्ट्रिंग विवरण से निकालने के लिए कुछ भी नहीं होने पर groupAs(".*") and का उपयोग करने की आवश्यकता को हटा दें।

उस मामले में यह लिखने के लिए पर्याप्त होना चाहिए:

val save: Given[Int] = groupAs(".*") and { (s: String) => 
    database.save(Entity(1)) === 1 
    1 
} 

एक और संभावना सीधे लिखने के लिए अनुमति देने के लिए किया जाएगा:

val save: Given[Int] = groupAs(".*") and { (s: String) => 
    database.save(Entity(1)) === 1 
} 

जहां एक Given[T] वस्तु एक String => MatchResult[T] से बनाया जा सकता क्योंकि MatchResult[T] ऑब्जेक्ट में पहले से ही T का मान है, जो "दिया गया" बन जाएगा।

2 - एक असफल उदाहरण

का उपयोग निहित WhenFailAround संदर्भ निश्चित रूप से आप क्या चाहते हैं (जब तक आप उम्मीदों विवरण के रूप में जी/डब्ल्यू/टी ऊपर दिखाए गए के साथ जाने क्या करने के लिए सबसे अच्छा तरीका है के बाद निष्पादन बंद करो उदाहरण)।

निम्न उदाहरण में दखल अगर समवर्ती उदाहरण के पिछले ब्लॉक में एक उदाहरण विफल रही द्वारा step(stepOnFail = true)

step(stepOnFail = true) कार्यों पर ध्यान दें। हालांकि, जब आप sequential का उपयोग कर रहे हैं, तो पिछला ब्लॉक केवल एक उदाहरण तक ही सीमित है। इसलिए आप क्या देख रहे हैं। असल में मुझे लगता है कि यह एक बग है और शेष सभी उदाहरण निष्पादित नहीं किए जाने चाहिए, भले ही आप क्रमिक उपयोग कर रहे हों या नहीं। इसलिए इस हफ्ते के अंत में आने वाले फिक्स के लिए ट्यून करें।

+0

1.12.3-SNAPSHOT के साथ '==>' का उपयोग करके, उदाहरण में ग्रुपिंग अपेक्षाएं अच्छी लगती हैं। परिणामस्वरूप कोड पढ़ने के लिए काफी आसान है, मुझे लगता है। साथ ही, टेस्ट कोड परिणामों के ऊपर सभी * आलसी vals * को कोड में रखना जो मुझे लगता है पढ़ने के लिए थोड़ा आसान है। - अगले कुछ महीनों में (जब 1.12.3 जारी किया गया है) में, मैं शायद एक समय में परीक्षण सूट का थोड़ा सा फिर से लिखूंगा, और '==>' का उपयोग करने पर विचार करें। (मैं इसे कई छोटे परीक्षण सूट में भी विभाजित कर दूंगा और उन्हें 'शामिल' कर दूंगा।) धन्यवाद – KajMagnus

+0

'चरण (stepOnFail = true)' पर नोट के लिए धन्यवाद। – KajMagnus

+0

सभी परिवर्तन 1.12.3-स्नैपशॉट में हैं: अब विफलताओं को दिए गए/कब/फिर चरणों से फेंक दिया जा सकता है, एक पूर्ण चरण सीधे पूर्ण विवरण स्ट्रिंग लेने वाले फ़ंक्शन से बनाया जा सकता है, चरण (stopOnFail = true) अपेक्षित व्यवहार करता है अनुक्रमिक विनिर्देश के साथ। – Eric

0

Specs doc कहा गया है कि यदि आप एक विफलता

"The second example will be skipped" >> { 
    1 === 2 
    (1 === 3).orSkip 
} 

के मामले में को .orSkip उपयोग कर सकते हैं उदाहरण के बाकी को छोड़ लेकिन मैं इसे व्यक्तिगत रूप से

+0

असल में केवल वर्तमान परीक्षण {{}} में शेष परीक्षणों को रद्द करता है। ब्लॉक (जिसे मुझे लगता है कि "* परीक्षण खंड *" कहा जाता है)। जो मैं खोज रहा हूं, वह कुछ है जो सभी गहराई से सभी परीक्षण टुकड़ों को मारता है (मेरा मतलब है, "गहराई" जहां टेक्स्ट "दूसरा उदाहरण ..." शुरू होता है, न कि "{गहराई" ...} 'ब्लॉक)। मैं इसे और स्पष्ट करने के लिए अपना प्रश्न अपडेट करूंगा। – KajMagnus

1

(के संबंध में प्रश्न प्रयास नहीं किया है 1: मुझे नहीं पता कि उदाहरण के अंदर var एस के लिए कुछ बेहतर विकल्प है या नहीं। शायद मेरे उदाहरण बहुत लंबे हैं, और शायद मुझे अपने स्पेक को कई छोटे चश्मे में विभाजित करना चाहिए।)

प्रश्न 2 के संबंध में, मैं in this email by etorreborre पाया बाद में परीक्षण रोक इसलिए की तरह किया जा सकता है कि:

"ex1" >> ok 
"ex2" >> ok 
"ex3" >> ko 
step(stopOnFail=true) 

"ex4" >> ok 

(EX4 अगर EX1, ex2 या ex3 विफल रहता है छोड़ दिया जाएगा। (यह Specs2 < 1.12.3 में होने की उम्मीद के रूप में आप एक अनुक्रमिक कल्पना उपयोग कर रहे हैं काम नहीं करता है, लेकिन।))


यहाँ एक और तरीका है: this Specs2 Googl groups email by etorreborre के अनुसार बाद में परीक्षण की विफलता पर रोक हो सकता है, जैसे इतना: ("example2" को छोड़ दिया जाएगा, लेकिन "Example3" और "4" चल पाएंगे)

class TestSpec extends SuperSpecification { 

    sequential 

    "system1" >> { 
     implicit val stop = WhenFail() 
     "example1" >> ko 
     "example2" >> ok 
    } 
    "system2" >> { 
     implicit val stop = WhenFail() 
     "example3" >> ok 
     "example4" >> ok 
    } 
} 

case class WhenFail() extends Around { 
    private var mustStop = false 

    def around[R <% Result](r: =>R) = { 
    if (mustStop)   Skipped("one example failed") 
    else if (!r.isSuccess) { mustStop = true; r } 
    else     r 
    } 
} 

this email by etorreborre में अगर बाद में विनिर्देशों को रद्द करने के एक विधि है एक n उदाहरण में विफल रहता है, यदि आप विनिर्देशों की सूची भी शामिल है:

sequential^stopOnFail^
"These are the selenium specifications"  ^
    include(childSpec1, childSpec2, childSpec3) 

और तुम build.sbt में परीक्षण विकल्पों को संपादित करने तो बच्चे चश्मा फिर से निष्पादित नहीं कर रहे हैं indepentendly के बाद वे शामिल किया गया है देख सकते हैं।ईमेल से:

testOptions := Seq(Tests.Filter(s => 
    Seq("Spec", "Selenium").exists(s.endsWith(_)) && 
    ! s.endsWith("ChildSpec")))