4

私はHaskellが初めてです。私は単純な関数を次のようにテストしていますTest.Framework:

import Test.Framework (defaultMain, testGroup)
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2 (testProperty)

import Test.QuickCheck
import Test.HUnit

data Kind = Variable
          | Const
          | Polymorphic
    deriving (Show, Eq, Ord)

calculate :: Int -> Kind -> Float

calculate quantity Variable =
    (**2) . fromIntegral $ quantity

calculate _ Const =
    10

calculate quantity Polymorphic =
    if quantity <= 10 then
        10
    else
        (**2) . fromIntegral $ quantity

prop_ValuePositive quantity kind =
    calculate quantity kind
 >= 0.0

test_ValueVariable1 =
    calculate 1 Variable
    @?= (**2) 1

test_ValueVariable2 =
    calculate 10 Variable
    @?= (**2) 10

test_ValueConst1 =
    calculate 1 Const
    @?= 10

test_ValueConst2 =
    calculate 10 Const
    @?= 10

test_ValuePolymorphic1 =
    calculate 1 Polymorphic
    @?= 10

test_ValuePolymorphic2 =
    calculate 11 Polymorphic
    @?= (**2) 11

instance Test.QuickCheck.Arbitrary Kind where
    arbitrary = Test.QuickCheck.oneof(
        [return Variable,
         return Const,
         return Polymorphic])

main = defaultMain tests

tests = [
    testGroup "Value" [
        testProperty "Value is positive" prop_ValuePositive,
        testCase "Value is calculated right for Variable"
            test_ValueVariable1,
        testCase "Value is calculated right for Variable"
            test_ValueVariable2,
        testCase "Value is calculated right for Const"
            test_ValueConst1,
        testCase "Value is calculated right for Const"
            test_ValueConst2,
        testCase "Value is calculated right for Polymorphic"
            test_ValuePolymorphic1,
        testCase "Value is calculated right for Polymorphic"
            test_ValuePolymorphic2
        ]
    ]

気になるのは、純粋な関数をQuickCheckプロパティでテストし、不純な関数をHUnitテストケースでテストすることが推奨されていることです。しかし、そのようにすると、プロパティの 3 つのケース ( ConstVariableおよびPolymorphic) ごとに関数定義を繰り返して、関数が想定どおりに返されることをテストする必要があります。私の意見では、それは重複が多すぎます。

prop_ValueVariable quantity Variable =
    calculate quantity Variable
 == ((**2) . fromIntegral $ quantity)

( のすべての場合についても同様Kind)

対照的に、現在のコードでは、(単体テストの精神で) 定義を実際に複製することなく、関数の「明白な」プロパティを 1 つだけテストし、関数が返す必要のある「サンプル ポイント」をいくつか提供します。

正しいアプローチとは?

  1. この関数のすべての側面をテストするためにプロパティを使用し、テストでその定義を複製する可能性があります
  2. プロパティは、返されるものの「プロパティ」のみに使用しますが、定義を複製せず、いくつかの単体テストを提供してください。
4

2 に答える 2

3

プロパティベースのテストは純粋なコード用であり、純粋でないコードの単体テストは有用なガイドラインですが、絶対的な真実ではありません。単体テストは、純粋なコードにも役立ちます。私は通常、単体テストから始めます。

describe "parseMarkdown" $ do
  it "parses links" $ do
    parseMarkdown "[foo](http://foo.com/)" `shouldBe` Link "http://foo.com" "foo"

後でそれをプロパティに抽象化します

  it "parses *arbitrary* links" $
    property $ \link@(Link url name) ->
      parseMarkdown "[" ++ name ++ "](" ++ url ++ ")" `shouldBe` link

ただし、(a) 適切なプロパティがないか、(b) プロパティによってテスト カバレッジが向上しないため、単体テストに固執することがあります。

一方、プロパティは純粋でないコードにも役立ちます。たとえば、プロパティを使用してデータベースの抽象化をテストしたい場合があります

describe "loadUser" $ do
  it "retrieves saved users from the database" $ do
    property $ \user -> do
      saveUser user >>= loadUser `shouldReturn` user
于 2013-09-08T18:29:02.103 に答える