1

この簡単な例を考えてみましょう:

  • オブジェがPersonある
  • 次のいずれかが必要です:FirstNameおよびLastName(または両方、ただし一方は必須)
  • 有効なAge値 (整数、0 から 150 の間)が必要です

この単純なケースをどのようにプロパティ ベース テストしますか?

4

1 に答える 1

4

全体的な設計アプローチは問題の言語の機能に完全に依存するため、言語に依存しない方法でこの質問に有意義に答えることはできないと思います。

たとえば、強力な静的型と合計型を持つ言語では、上記の要件のほとんどは、型システムを使用して宣言的にモデル化できます。F# の例を次に示します。

type Name =
| FirstName of string
| LastName of string
| FullName of string * string

このNameタイプには、名、姓、またはその両方のみを含めることができます。要件に従わない値を作成することはできません。

次の Age 型のケース コンストラクターは、型を別のモジュールに配置することで非表示にできます。そのモジュールが以下のtoAge(and getAge) 関数のみをエクスポートする場合、Age値を作成する唯一の方法は を呼び出すことtoAgeです。

type Age = Age of int

let toAge x =
    if 0 <= x && x <= 150
    then Some (Age x)
    else None

let getAge (Age x) = x

Personこれらの補助型を使用して、型を定義できるようになりました。

type Person = { Name : Name; Age : Age }

要件のほとんどは、型システムに組み込まれています。タイプの無効な値を作成することはできませんPerson

失敗する可能性がある唯一の動作はtoAge関数に含まれているため、意味のあるプロパティベースのテストを受けることができる唯一の動作です。FsCheck を使用した例を次に示します。

open System
open FsCheck
open FsCheck.Xunit
open Swensen.Unquote

[<Property(QuietOnSuccess = true)>]
let ``Value in valid age range can be turned into Age value`` () =
    Prop.forAll (Gen.choose(0, 150) |> Arb.fromGen) (fun i ->
        let actual = toAge i
        test <@ actual |> Option.map getAge |> Option.exists ((=) i) @>)

[<Property(QuietOnSuccess = true)>]
let ``Value in invalid age range can't be turned into Age value`` () =
    let tooLow = Gen.choose(Int32.MinValue, -1)
    let tooHigh = Gen.choose(151, Int32.MaxValue)
    let invalid = Gen.oneof [tooLow; tooHigh] |> Arb.fromGen
    Prop.forAll invalid (fun i ->

        let actual = toAge i

        test <@ actual |> Option.isNone @>)

おわかりのように、有効な入力値と無効な入力値の 2 つのケースをテストします。これは、これらのケースごとにジェネレーターを定義し、actual値を検証することによって行われます。

于 2015-09-30T16:04:36.047 に答える