我在工作中第一次深入研究 F#,我正在将几个 C# 单元测试转移到 F# 作为练习。我们的测试相当复杂,但我喜欢挑战(包括设置、继承、拆卸等)。
正如我所看到的,如果可能的话,应该避免可变性,但是在编写 [SetUp]
时部分测试我似乎无法找到跳过可变性的方法。为测试创建虚拟 XML 的示例:
[<TestFixture>]
type CaseRuleFixture() =
[<DefaultValue>] val mutable xsl : XNamespace
[<DefaultValue>] val mutable simpleStylesheet : XElement
[<DefaultValue>] val mutable testNode : XElement
[<DefaultValue>] val mutable rootNode : XElement
[<DefaultValue>] val mutable root : XElement
let CreateXsltHeader(xsl: XNamespace) =
// Build XSLT header
let styleSheetRoot =
new XElement(
xsl + "stylesheet",
new XAttribute(XName.Get "version", "1.0"),
new XAttribute(XNamespace.Xmlns + "xsl", "http://www.w3.org/1999/XSL/Transform"),
new XAttribute(XNamespace.Xmlns + "msxsl", "urn:schemas-microsoft-com:xslt"),
new XAttribute(XName.Get "exclude-result-prefixes", "msxsl"),
new XAttribute(XNamespace.Xmlns + "utils", "urn:myExtension"))
let outputNode =
new XElement(
xsl + "output",
new XAttribute(XName.Get "method", "xml"),
new XAttribute(XName.Get "indent", "yes"))
styleSheetRoot.Add outputNode
styleSheetRoot
[<SetUp>]
member this.SetUp() =
this.xsl <- XNamespace.Get "http://www.w3.org/1999/XSL/Transform"
this.simpleStylesheet <- CreateXsltHeader(this.xsl)
Directory.EnumerateFiles "Templates"
|> Seq.iter(fun filepath -> this.simpleStylesheet.Add(XElement.Parse(File.ReadAllText filepath).Elements()))
let variable =
new XElement(
this.xsl + "variable",
new XAttribute(XName.Get "name", "ROOT"),
new XAttribute(XName.Get "select", "ROOT"))
this.simpleStylesheet.Add(variable)
let rootTemplate = new XElement(this.xsl + "template", new XAttribute(XName.Get "match", "/ROOT"))
this.simpleStylesheet.Add(rootTemplate);
this.rootNode <- new XElement(XName.Get "ROOT")
rootTemplate.Add(this.rootNode);
this.root <- new XElement(XName.Get "ROOT")
this.testNode <- new XElement(XName.Get "TESTVALUE")
this.root.Add(this.testNode)
[<Test>]
member this.CaseCapitalizeEachWordTest() =
this.testNode.Value <- " text to replace ";
let replaceRule = new CaseRule();
replaceRule.Arguments <- [| "INITIALS" |];
this.rootNode.Add(
replaceRule.ApplyRule [| new XElement(this.xsl + "value-of", new XAttribute(XName.Get "select", "TESTVALUE")) |]);
let parser = new XsltParserHelper(this.simpleStylesheet);
let result = parser.ParseXslt(this.root);
let value = result.DescendantsAndSelf() |> Seq.find(fun x -> x.Name = XName.Get "ROOT")
Assert.AreEqual(" Text To Replace ", value.Value)
那些
[<DefaultValue>] val mutable
声明变量(不初始化,因为这是 SetUp 工作)并使这些变量可用于所有类范围,事实上我基本上已经从 C# 中进行了 1:1 的转换,而没有任何明显的语法和可读性让我不寒而栗。有没有办法重写这些看起来更好的测试和设置?因为我在互联网上看到的所有例子都很简单,很小,不包括这些情况。
最佳答案
让我们首先将问题缩小到更易于管理的规模:
减少问题
在此测试中,您在 SetUp
中初始化了两个可变字段。方法:
[<TestFixture>]
type MutableTests() =
[<DefaultValue>] val mutable foo : int
[<DefaultValue>] val mutable bar : int
[<SetUp>]
member this.SetUp () =
this.foo <- 42
this.bar <- 1337
[<Test>]
member this.TheTest () =
Assert.AreEqual(42, this.foo)
Assert.AreEqual(1337, this.bar)
显然,这是对真正问题的替代。
返回值的函数
与其设置类字段,不如编写函数来初始化您需要的值?
module BetterTests =
let createDefaultFoo () = 42
let createDefaultBar () = 1337
[<Test>]
let ``a test using individual creation functions`` () =
let foo = createDefaultFoo ()
let bar = createDefaultBar ()
Assert.AreEqual(42, foo)
Assert.AreEqual(1337, bar)
如果您不想一次获得所有值(就像您可以访问类中的所有字段一样),您可以定义一个函数来返回元组或记录中的所有值:
let createAllDefaultValues () = createDefaultFoo (), createDefaultBar ()
[<Test>]
let ``a test using a single creation function`` () =
let foo, bar = createAllDefaultValues ()
Assert.AreEqual(42, foo)
Assert.AreEqual(1337, bar)
此示例使用
int * int
元组,但定义记录可能更具可读性: type TestValues = { Foo : int; Bar : int }
let createDefaultTestValues () = {
Foo = createDefaultFoo ()
Bar = createDefaultBar () }
[<Test>]
let ``a test using a single creation function that returns a record`` () =
let defaultValues = createDefaultTestValues ()
Assert.AreEqual(42, defaultValues.Foo)
Assert.AreEqual(1337, defaultValues.Bar)
请注意,与 C# 中的类不同,F# 中的记录声明是超轻量级的。
如果您想了解有关使用 F# 进行惯用单元测试的更多信息,可以从 my Pluralsight course about unit testing with F# 开始。 .
关于unit-testing - F# - 如何使用 NUnit 正确实现设置和初始化变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32459246/