首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在调用测试方法之前静态地初始化测试数据?

如何在调用测试方法之前静态地初始化测试数据?
EN

Stack Overflow用户
提问于 2018-03-09 00:07:19
回答 1查看 409关注 0票数 3

我们已经设置了下面的测试。

Permutations.Tests.fsproj

代码语言:javascript
复制
<ItemGroup>
  <Compile Include="Permute1Tests.fs" />
  <Compile Include="Permute2Tests.fs" />
</ItemGroup>

Permute1Tests.fs

代码语言:javascript
复制
module Permute1Tests

open Xunit
open Permutations.Permute1

[<Theory>]
[<MemberData("permuteTestValues")>]
let ``permute`` (x, expected) =
    let actual = permute x
    Assert.Equal<List<int>>(expected, actual);

let permuteTestValues : obj array seq =
    seq {
        yield [| [0;1]; [[0;1]; [1;0]] |]
    }

Permute2Tests.fs

代码语言:javascript
复制
module Permute2Tests

open Xunit
open Permutations.Permute2

[<Theory>]
[<MemberData("removeFirstTestData")>]
let ``removeFirst`` (item, list, expected: List<int>) =
    let actual = removeFirst list item
    Assert.Equal<List<int>>(expected, actual)

let removeFirstTestData : obj array seq =
    seq {
        yield [| 0; [1;2;3;4]; [1;2;3;4] |]
    }

当我们运行dotnet test时,这是一个错误:

System.InvalidOperationException : Permute2Tests.removeFirst返回的测试数据为null。请确保在调用此测试方法之前对其进行静态初始化。

奇怪的是,Permute1Tests.fs运行时没有错误。它的测试通过了。而且,如果我们将Permute1Test.fsItemGroup中的位置与Permute2Test.fs交换,那么后者现在起作用了,而前者有错误。

如何在调用测试方法之前静态地初始化测试数据?在我们当前的方法中,ItemGroup顺序似乎很重要,这使得我们当前的方法失败了。

上述代码就在这里的完整版本。

编辑: ILSpy输出

Permute1Tests.fs.cs

代码语言:javascript
复制
// <StartupCode$Permutations-Tests>.$Permute1Tests
using <StartupCode$Permutations-Tests>;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

internal static class $Permute1Tests
{
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    internal static readonly IEnumerable<object[]> permuteTestValues@12;

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    [CompilerGenerated]
    [DebuggerNonUserCode]
    internal static int init@;

    static $Permute1Tests()
    {
        IEnumerable<object[]> permuteTestValues = 
            $Permute1Tests.permuteTestValues@12 = 
                (IEnumerable<object[]>)new Permute1Tests.permuteTestValues@14(0, null);
    }
}

Permute2Tests.fs.cs

代码语言:javascript
复制
// <StartupCode$Permutations-Tests>.$Permute2Tests
using <StartupCode$Permutations-Tests>;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

internal static class $Permute2Tests
{
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    internal static IEnumerable<object[]> removeFirstTestData@15;

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    [CompilerGenerated]
    [DebuggerNonUserCode]
    internal static int init@;

    public static void main@()
    {
        IEnumerable<object[]> removeFirstTestData = 
            $Permute2Tests.removeFirstTestData@15 = 
                (IEnumerable<object[]>)new Permute2Tests.removeFirstTestData@17(0, null);
    }
}

Permutations.Test.fsproj

代码语言:javascript
复制
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Permute1Tests.fs" />
    <Compile Include="Permute2Tests.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
    <PackageReference Include="xunit" Version="2.3.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
    <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Permutations\Permutations.fsproj" />
  </ItemGroup>

</Project>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-03-09 20:42:47

为什么

这与您的程序集是“可执行的”(即带有入口点的程序)而不是“库”这一事实有关。

F#编译可执行文件的方式与库略有不同:在入口点所在的模块中,所有静态数据都在main函数中初始化,然后再执行其他所有操作;在所有其他模块中,静态数据在静态构造函数中初始化。我不知道这个决定背后的理由是什么,但这就是F#编译器的行为方式。

接下来,F#编译器如何确定哪个模块包含入口点?非常简单:无论哪个模块是最后一个模块,都是入口点所在。想想看,这是唯一明智的选择:因为F#有编译顺序,只有最后一个文件才能访问所有其他文件中的定义;因此,入口点必须在这里。

因此,在您的示例中,无论哪个模块在列表中最后一个,最终都会得到一个main函数,静态初始化代码位于该函数中。而且,由于单元测试运行程序在执行测试之前不运行入口点,所以该模块中的静态数据仍未初始化。

解决方案1:添加一个人工模块以包含入口点

正如您自己已经发现的,一个解决方案是添加一个只包含入口点的人工模块。这样,测试模块将不再是最后一个,不再包含入口点,因此它的数据将在静态构造函数中初始化。

人工模块甚至不需要有一个[<EntryPoint>] main函数,它可以是:

代码语言:javascript
复制
module Dummy
let _x = 0  // `do ()` would be even shorter, but that will create a warning

无论如何,编译器将添加一个入口点。

解决方案2:编译为netstandard2.0

如果将目标从netcoreapp2.0切换到netstandard2.0,则程序集将被视为“库”而不是“可执行文件”,编译器不会添加入口点,也不会将静态初始化放在其中。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49184594

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档