1

考慮這個簡單的例子:屬性基於測試爲簡單對象驗證

  • theres一個Person對象
  • 它必須具有的一種:FirstNameLastName(或兩者,但一個是強制性的)
  • 它必須有一個有效的Age(整數,介於0和150)

你將如何財產基地測試這個簡單的例子?

回答

4

我不認爲你可以用與語言無關的方式有意義地回答這個問題,因爲總體設計方法完全取決於有問題的語言的能力。

例如,在具有強靜態類型和求和類型的語言中,大多數上述要求可以使用類型系統以聲明方式建模。這裏有一個F#例如:

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

Name類型只能包含一個名字或姓氏,或兩者兼而有之。創建不符合要求的值是不可能的。

以下年齡類型的案例構造函數可以通過將類型置於單獨的模塊中來隱藏。如果該模塊僅導出下面的toAge(和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 @>) 

正如你所知道的,它測試兩種情況:有效的輸入值,和無效的輸入值。它通過爲每種情況定義生成器,然後驗證actual值。

+0

感謝馬克,我不知道F#但似乎你可以創建一個無效的人也傳遞空的字符串我是否正確?我想在這種情況下,你會像檢查年齡範圍一樣檢查他們的長度,對吧? –

+1

正確的,如果空字符串是無效的(這是合理的),那麼這個方法和年齡相同。 –