0

我正在尝试学习 F# 并开始尝试一些简单的类,但这些类的行为并不像预期的那样。

我一定是误解了一些简单的基本原理。如果这个问题太基本了,我们深表歉意,但我们将不胜感激。

无论如何,我第一次应用了测试驱动的开发方法,现在我更好地体会到了它的好处。

以下源码探索:

  1. 定义一个实用函数 (optionIntTo_s)。
  2. 定义类 TUnitTester - 其目的是协助进行单元测试。
  3. 定义实验类 TOrderItem - 它包含一个可变选项字段 fquantity(表示 SQL null else 整数值)。
  4. 定义另一个实验类 TOrderItems - 它包装了一个 TOrderItem 对象数组。
  5. 对 TOrderItem 类进行 4 次测试;这些都按预期工作。
  6. 对 TOrderItems 类进行另外 10 次测试;其中两个失败(总共进行了 14 次测试中的第 6 次和第 8 次)!

任何帮助解释这些失败的测试将不胜感激。

这是源代码,然后是我得到的输出:

//-----------------------------------------------------------------------------------------------------------------
// Utility Funcions
//-----------------------------------------------------------------------------------------------------------------
let optionIntTo_s(aValue : option<int>) = 
    match aValue with
    | None    -> "null"
    | Some(_) -> sprintf "%d" (Option.get(aValue))


//-----------------------------------------------------------------------------------------------------------------
// Class TUnitTester
//-----------------------------------------------------------------------------------------------------------------
type TUnitTester = class
    //----------------------------------
    // Field Definitions
    //----------------------------------
    val mutable ftestCount : int
    val mutable fsuccessCount : int


    //----------------------------------
    // Constructors
    //----------------------------------
    new ()  =
        {ftestCount = 0;
         fsuccessCount = 0}


    //----------------------------------
    // Methods
    //----------------------------------
    member this.assertEqual(aTestValue, aExpectation, aDescription) = 
        this.ftestCount <- this.ftestCount + 1

        if (aTestValue = aExpectation) then
            this.fsuccessCount <- this.fsuccessCount + 1
            printfn "Test %d: assertEqual succeeded: expected %A: got %A; %s" this.ftestCount aExpectation aTestValue aDescription
        else
            printfn "Test %d: assertEqual failed:    expected %A: got %A; %s" this.ftestCount aExpectation aTestValue aDescription


    member this.assertFalse(aDescription, aTestValue : bool) = 
        this.ftestCount <- this.ftestCount + 1

        if (aTestValue) then
            this.fsuccessCount <- this.fsuccessCount + 1
            printfn "assertFalse succeeded: expected false: got false; %s" aDescription
        else
            printfn "assertFalse failed:    expected false: got true;  %s" aDescription


    member this.assertTrue(aDescription, aTestValue : bool) =
        this.ftestCount <- this.ftestCount + 1

        if (aTestValue) then
            this.fsuccessCount <- this.fsuccessCount + 1
            printfn "assertTrue  succeeded: expected true: got true;  %s" aDescription
        else
            printfn "assertTrue  failed:    expected true: got false; %s" aDescription


    member this.printStatistics() =
        printfn "total tests = %d; successes = %d; failures = %d"  this.ftestCount this.fsuccessCount (this.ftestCount - this.fsuccessCount)
end;;


//-----------------------------------------------------------------------------------------------------------------
// Class TOrderItem
//    Ref: "F# option cheat sheet" - http://missingfaktor.blogspot.com.au/2012/02/f-option-cheat-sheet.html
//-----------------------------------------------------------------------------------------------------------------
type TOrderItem = class
    //----------------------------------
    // Field Definitions
    //----------------------------------
    val mutable fquantity : option<int>


    //----------------------------------
    // Properties
    //----------------------------------
    member this.quantity  with get() = Option.get(this.fquantity) and 
                               set(aValue : int) = this.fquantity <- Some(aValue)

    member this.isNull    with get() = Option.isNone(this.fquantity)
    member this.isNotNull with get() = Option.isSome(this.fquantity)


    //----------------------------------
    // Constructors
    //----------------------------------
    new ()  =
        {fquantity = None;}

    new (aValue : int)  =
        {fquantity = Some(aValue);}


    //----------------------------------
    // Methods
    //----------------------------------
    member this.to_s() = optionIntTo_s(this.fquantity)
end;;



//-----------------------------------------------------------------------------------------------------------------
// Class TOrderItems
//-----------------------------------------------------------------------------------------------------------------
type TOrderItems = class
    //----------------------------------
    // Field Definitions
    //----------------------------------
    val fOrderItems : TOrderItem []


    //----------------------------------
    // Properties
    //----------------------------------
    member this.orderItems with get() = this.fOrderItems


    //----------------------------------
    // Constructors
    //----------------------------------
    new (aLength : int)  =
        { fOrderItems = Array.create aLength (TOrderItem())}


    //----------------------------------
    // Methods
    //----------------------------------
    member this.Item i = this.fOrderItems.[i]     //Translates to the operator this.[i]

    member this.setAll(aValue : int) = 
        for i = 0 to (Array.length this.fOrderItems) - 1 do
            this.fOrderItems.[i].quantity <- aValue

    member this.setFromCombination(aScale1, aScale2, (aOrderItems1 : TOrderItems), (aOrderItems2 : TOrderItems)) =
        for i = 0 to (Array.length this.fOrderItems) - 1 do
            this.fOrderItems.[i].quantity <- aScale1 * aOrderItems1.[i].quantity + aScale2 * aOrderItems2.[i].quantity

    member this.setFromDifference((aOrderItems1 : TOrderItems), (aOrderItems2 : TOrderItems)) =
        this.setFromCombination(1, -1, aOrderItems1, aOrderItems2)

    member this.setFromSum((aOrderItems1 : TOrderItems), (aOrderItems2 : TOrderItems)) =
        this.setFromCombination(1, 1, aOrderItems1, aOrderItems2)

    member this.setItem i (aValue : int) =         //TODO NUKE THIS METHOD LATER....
        this.fOrderItems.[i].quantity <- aValue

    member this.to_s() = 
        Array.fold (fun (accumulator : string) (item : TOrderItem) -> accumulator + item.to_s() + "; ") "" this.fOrderItems
        |> (fun (x) -> "[| " + x + "|]")
end;;


//-----------------------------------------------------------------------------------------------------------------
// Test Classes TOrderItem and TOrderItems
//    (A) Create unit tester instance.
//-----------------------------------------------------------------------------------------------------------------
let myTester = TUnitTester()


//-----------------------------------------------------------------------------------------------------------------
//    (B) Test Class TOrderItem
//-----------------------------------------------------------------------------------------------------------------
let myOrderItem = TOrderItem()
myTester.assertEqual(myOrderItem.to_s(), "null", "myOrderItem.to_s()")        //Test 1 - OK

myOrderItem.quantity <- 8000
myTester.assertEqual(myOrderItem.quantity, 8000, "myOrderItem.quantity")      //Test 2 - OK

myOrderItem.quantity <- 9000
myTester.assertEqual(myOrderItem.quantity, 9000, "myOrderItem.quantity")      //Test 3 - OK
myTester.assertEqual(myOrderItem.to_s(), "9000", "myOrderItem.to_s()")        //Test 4 - OK


//-----------------------------------------------------------------------------------------------------------------
//    (C) Test Class TOrderItems
//-----------------------------------------------------------------------------------------------------------------
let myOrderItems = TOrderItems(4)
myTester.assertEqual(myOrderItems.to_s(), "[| null; null; null; null; |]", "myOrderItems.to_s()")  //Test 5 - OK

myOrderItems.[2].quantity <- 1000
myTester.assertEqual(myOrderItems.to_s(), "[| null; null; 1000; null; |]", "myOrderItems.to_s()")  //Test 6 - Fails!

myOrderItems.setAll 5000
myTester.assertEqual(myOrderItems.[2].quantity, 5000, "myOrderItems.[2].quantity")                 //Test 7 - OK

myOrderItems.setItem 1 9999
myTester.assertEqual(myOrderItems.[0].quantity , 5000, "myOrderItems.[0].quantity")                //Test 8 - Fails!
myTester.assertEqual(myOrderItems.[1].quantity , 9999, "myOrderItems.[1].quantity")                //Test 9 - OK


let myOrderItems1 = TOrderItems(4)
let myOrderItems2 = TOrderItems(4)
let myOrderItems3 = TOrderItems(4)
let myOrderItems4 = TOrderItems(4)
let myOrderItems5 = TOrderItems(4)

myOrderItems1.setAll 5000
myOrderItems2.setAll 4000

myTester.assertEqual(myOrderItems1.to_s(), "[| 5000; 5000; 5000; 5000; |]", "myOrderItems1.to_s()")      //Test 10 - OK
myTester.assertEqual(myOrderItems2.to_s(), "[| 4000; 4000; 4000; 4000; |]", "myOrderItems2.to_s()")      //Test 11 - OK


myOrderItems3.setFromCombination(2, 3, myOrderItems1, myOrderItems2)
myTester.assertEqual(myOrderItems3.to_s(), "[| 22000; 22000; 22000; 22000; |]", "myOrderItems3.to_s()")  //Test 12 - OK


myOrderItems4.setFromSum(myOrderItems1, myOrderItems2)
myTester.assertEqual(myOrderItems4.to_s(), "[| 9000; 9000; 9000; 9000; |]", "myOrderItems4.to_s()")      //Test 13 - OK


myOrderItems5.setFromDifference(myOrderItems1, myOrderItems2)
myTester.assertEqual(myOrderItems5.to_s(), "[| 1000; 1000; 1000; 1000; |]", "myOrderItems5.to_s()")      //Test 14 - OK


//-----------------------------------------------------------------------------------------------------------------
//    (D) Report test statistics.
//-----------------------------------------------------------------------------------------------------------------
myTester.printStatistics()

最后,这是我在 MAC OS X 上的 mono 下从 Microsoft (R) F# 2.0 Compiler build 2.0.0.0 获得的输出如下:

Microsoft (R) F# 2.0 Compiler build 2.0.0.0
Copyright (c) Microsoft Corporation. All Rights Reserved.
Test 1: assertEqual succeeded: expected "null": got "null"; myOrderItem.to_s()
Test 2: assertEqual succeeded: expected 8000: got 8000; myOrderItem.quantity
Test 3: assertEqual succeeded: expected 9000: got 9000; myOrderItem.quantity
Test 4: assertEqual succeeded: expected "9000": got "9000"; myOrderItem.to_s()
Test 5: assertEqual succeeded: expected "[| null; null; null; null; |]": got "[| null; null; null; null; |]"; myOrderItems.to_s()
Test 6: assertEqual failed:    expected "[| null; null; 1000; null; |]": got "[| 1000; 1000; 1000; 1000; |]"; myOrderItems.to_s()
Test 7: assertEqual succeeded: expected 5000: got 5000; myOrderItems.[2].quantity
Test 8: assertEqual failed:    expected 5000: got 9999; myOrderItems.[0].quantity
Test 9: assertEqual succeeded: expected 9999: got 9999; myOrderItems.[1].quantity
Test 10: assertEqual succeeded: expected "[| 5000; 5000; 5000; 5000; |]": got "[| 5000; 5000; 5000; 5000; |]"; myOrderItems1.to_s()
Test 11: assertEqual succeeded: expected "[| 4000; 4000; 4000; 4000; |]": got "[| 4000; 4000; 4000; 4000; |]"; myOrderItems2.to_s()
Test 12: assertEqual succeeded: expected "[| 22000; 22000; 22000; 22000; |]": got "[| 22000; 22000; 22000; 22000; |]"; myOrderItems3.to_s()
Test 13: assertEqual succeeded: expected "[| 9000; 9000; 9000; 9000; |]": got "[| 9000; 9000; 9000; 9000; |]"; myOrderItems4.to_s()
Test 14: assertEqual succeeded: expected "[| 1000; 1000; 1000; 1000; |]": got "[| 1000; 1000; 1000; 1000; |]"; myOrderItems5.to_s()
total tests = 14; successes = 12; failures = 2
4

1 回答 1

4

改变

Array.create aLength (TOrderItem())

Array.init aLength (fun t -> TOrderItem())

第一个创建一个包含许多副本的数组 - 它真的是为使用而设计的int,第二个做你想要它做的事情

此外,将来它有助于发布更少的带有问题的代码以使其更简单

于 2012-07-06T10:27:38.253 回答