dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.87k stars 783 forks source link

let bindings in modules not being initialized in dotnet core xunit test #2128

Closed jlewicki closed 7 years ago

jlewicki commented 7 years ago

Apologies if this is not the right repo for a report like this.

In porting a test project to the latest version of the dotnet cli tooling: v1.0.0-preview4-004233, I am experiencing unit test failures having to do with let bindings in modules not being initialized before my test methods are called. This is leading to NullReferenceExceptions and failed tests.

Repro steps

  1. Create a new F# unit test project dotnet new -l F# -t xunittest

  2. In the generated Tests.fs file, define a simple single case DU, and add a let binding for a value of this DU type.

  3. Add a test case that tests the let binding for null.

Example:

`

module Tests = 

    open System
    open Xunit

    type FooDU = FooDU of string
    let foo = FooDU "Foo!"

    [<Fact>]
    let ``My test`` () =
        Assert.NotNull foo

`

Expected behavior

The NotNull assertion should succeed.

Actual behavior

The foo binding is not initialized and the NotNull assertion fails .

Known workarounds

Adding an 'empty' .fs file as the last compilation unit in the project appears to entice the compiler into generating a cctor for the module containing the let binding, and consequently preventing the null reference. Something like: `

module Empty = 

    let private foo= ""

`

Related information

I'm not familiar with the compiler, but IlxGen.fs#L5909 seems like it may be relevant, perhaps because -t xunittest generates a project file with <OutputType>Exe</OutputType>?

smoothdeveloper commented 7 years ago

@jlewicki I think you might want to check that issue as well: #1371

.exe which don't have [<EntryPoint>] defined will have issues with static initialization of modules which is supposed to happen at the entry point.

I'm not sure if defining a moot entry point is a good workaround besides the one you mention.

This is definitely something that should be considered for a fix for .net core, I'm not sure of the reason why test assembly compiles to .exe (and command line compiles to .dll IIRC???).

jlewicki commented 7 years ago

Thanks and yes #1371 does appear to be basically the same issue.

I think I wouldn't have been so confused if I had been building my own .exe. The fact that donet new -t xunittest appears to create an exe implicitly really caught me off guard.

Hopefully this can be addressed or at least be made more apparent that it's quite easy to step on a land mine with a new unit test project.

KevinRansom commented 7 years ago

close as dupe

tytusse commented 3 years ago

Here I have a library project with same problem: https://github.com/tytusse/fsharp-xunit-startupcode-fails I added gh-action to run tests, you can see effects there: https://github.com/tytusse/fsharp-xunit-startupcode-fails/runs/3505966632?check_suite_focus=true

Workaround for that is adding dummy entry-point to library, .i.e.:

[<EntryPoint>]
let main (args:string[]) = 0

This was working fine in "older versions" of SDK's/libs - for my case it was net452 with xunit "2.3.1"