我正在尝试学习 F# 并开始尝试一些简单的类,但这些类的行为并不像预期的那样。
我一定是误解了一些简单的基本原理。如果这个问题太基本了,我们深表歉意,但我们将不胜感激。
无论如何,我第一次应用了测试驱动的开发方法,现在我更好地体会到了它的好处。
以下源码探索:
- 定义一个实用函数 (optionIntTo_s)。
- 定义类 TUnitTester - 其目的是协助进行单元测试。
- 定义实验类 TOrderItem - 它包含一个可变选项字段 fquantity(表示 SQL null else 整数值)。
- 定义另一个实验类 TOrderItems - 它包装了一个 TOrderItem 对象数组。
- 对 TOrderItem 类进行 4 次测试;这些都按预期工作。
- 对 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