2012-11-02 6 views
7

ich zur Zeit den folgenden Testcode:Ist es möglich, HUnit mit Test-Framework in einer anderen Monade als IO zu verwenden?

testUpdate :: Test 
testUpdate = testCase "update does change artist" $ do 
    (created, Just revised, parents) <- mbTest $ do 
    Just editor <- fmap entityRef <$> findEditorByName "acid2" 

    created <- create editor startWith 
    let artistId = coreMbid created 

    newRev <- update editor (coreRevision created) expected 

    editId <- openEdit 
    includeRevision editId newRev 
    apply editId 

    found <- findLatest artistId 
    parents <- revisionParents newRev 

    return (created, found, parents) 

    coreData revised @?= expected 

    assertBool "The old revision is a direct parent of the new revision" $ 
    parents == [coreRevision created] 

    where 
    startWith = ... 
    expected = ... 

Dieses irgendwie funktioniert, aber es ist chaotisch. Viel lieber wäre ich in der Lage, etwas zu schreiben, ohne die verschiedenen zu testenden Dinge zurückgeben zu müssen und stattdessen die Behauptungen zu haben, wo sie Sinn machen.

Ich sehe dort ist die Assertable Klasse, aber es scheint, als würde ich am Ende neu erfinden eine Menge Zeug.

+0

Große Frage, ich erinnere mich daran zu fragen, warum alles IO in seiner Art benötigt, wenn ich es zuerst verwendete. –

+0

Unterstützt das Monad 'liftIO'? – hammar

+0

@hammar Es tut, und ich bin mir nicht sicher, wie ich die Tatsache verpasst habe, dass alles, was ich tun muss, ist, diese Tests mit 'liftIO' zu hissen. Allerdings lasse ich die Frage offen, vielleicht gibt es andere Möglichkeiten :) – ocharles

Antwort

1

Warum nicht einfach Ihre Monade eine IO-Berechnung des Typs IO a zurückgeben, die ein reiner Wert ist? Da, wie in Ihrem Kommentar, der Fall, wenn die Monade eine Instanz von MonadIO ist trivial, nehmen , dass die Monade reine Berechnung erlaubt:

newtype M a = M {runM :: ....} 
instance Monad M where 
    ... 

makeTest :: M Assertion 
makeTest = do 
    created <- .. 
    found <- .. 
    parents <- .. 
    let test1 = coreData revised @?= expected 
    ... 
    let test2 = assertBool "The old revision..." $ 
        parents == [coreRevision create] 

    return $ test1 >> test2 

testUpdate :: Test 
testUpdate = testCase "update does change artist" $ runM makeTest 

Ein Bonus ist, dass Sie eine Sammlung von Tests, die von einer monadischen zurückkehren konnten Berechnung, genau wie Sie in der Liste Monade.

+0

Ich mag das nicht wirklich, weil alle Behauptungen viel später als der Code laufen, der von der Behauptung abhängt. Dies bedeutet wahrscheinlich, dass die Assertion nicht wirklich fehlschlägt, da der Code selbst möglicherweise bereits eine Ausnahme ausgelöst hat, weil die Assertion nicht gültig war. – ocharles

+0

Meinst du, dass der Code zwischen 'test1' und' test2' in meinem Beispiel eine Ausnahme fangen sollte, die von diesen Tests ausgelöst wird? Aber in diesem Fall muss Ihre Monade eine Instanz von MonadIO sein, es sei denn Sie betrügen (d. H. Mit 'unsicheren *') weil reiner Code in Haskell keine Ausnahme abfangen kann. Wie Sie sagten, ist dieser Fall mit "liftIO" trivial zu lösen. Vielleicht vergessen Sie, dass Haskell eine _lazy_ Sprache ist? Die Aufgabe einer faulen Sprache ist es, sicherzustellen, dass die Reihenfolge der Bewertung keine Rolle spielt (außer der IO-Sache). Ich würde gerne ein Beispiel sehen, wo weder 'liftIO' noch meine Lösung funktionieren. – mnish