0

I have written this simple test case for my code

module CustomerTests
open Xunit
open FsUnit
open MyProject.Customer
open MyProject.Customer.Domain
    
module ``When upgrading customer`` = 
  let customerVIP = {Id = 1; IsVip = true; Credit = 0.0M}
  let customerSTD = {Id = 2; IsVip = false; Credit = 100.0M}
  [<Fact>]
  let ``should give VIP customer more credit`` () =
    let expected = {customerVIP with Credit = customerVIP.Credit + 100.0M }
    let actual = upgradeCustomer customerVIP
    actual |> should equal expected

Very surprisingly this code fails with error

[xUnit.net 00:00:00.64] CustomerTests+When upgrading customer.should give VIP cstomer more credit [FAIL]
  Failed CustomerTests+When upgrading customer.should give VIP cstomer more credit [3 ms]
  Error Message:
  System.NullReferenceException : Object reference not set to an instance of an object.
  Stack Trace:
    at CustomerTests.When upgrading customer.should give VIP cstomer more credit() in /Users/user/code/fsharp/CustomerProject/CustomerTests.fs:line 12

But line 12 is just a record being created so its not possible for that line to throw an Object reference not set to an instance of object. This is totally puzzling me.

In the dotnet fsi repl I can execute all my method and there is no object reference problem in my function which are being called from my tests here.

Knows Not Much
  • 30,395
  • 60
  • 197
  • 373

1 Answers1

2

As this SO answer explains, XUnit loads the test in a way that skips initialization of those values. One easy fix is to use a class instead of a module:

type ``When upgrading customer``() =

    let customerVIP = {Id = 1; isVip = true; Credit = 0.0M}
    let customerSTD = {Id = 2; isVip = false; Credit = 100.0M}

    [<Fact>]
    let ``should give VIP cstomer more credit`` () =
        let expected = {customerVIP with Credit = customerVIP.Credit + 100.0M }
        let actual = upgradeCustomer customerVIP
        actual |> should equal expected

That way, initialization of those values happens as it should.

Brian Berns
  • 15,499
  • 2
  • 30
  • 40
  • yes this works. but also gives a warning `warning FS0988: Main module of program is empty: nothing will happen when it is run` any idea how to write test without the warning? – Knows Not Much Oct 28 '22 at 02:41
  • 1
    I think that happens because you've defined your project as a Console Application (.exe), rather than a Class Library (.dll). You can change the project type by removing this line from the .fsproj file: `Exe` – Brian Berns Oct 28 '22 at 02:48
  • That does not work. (sorry I am a newbie) if I remove that line and run `dotnet test` I get error `Testhost process for source(s) '/Users/abhisheksrivastava/code/fsharp/CustomerProject/bin/Debug/net6.0/CustomerProject.dll' exited with error: Error: An assembly specified in the application dependencies manifest (CustomerProject.deps.json) was not found: package: 'FSharp.Core', version: '6.0.6' path: 'lib/netstandard2.1/FSharp.Core.dll' . Please check the diagnostic logs for more information. Test Run Aborted.` – Knows Not Much Oct 28 '22 at 02:51
  • 1
    Hmm, I'm not sure why that would happen. Try switching the project back to a console app again, and just add this line at the bottom of your test file: `module Program = [] let main args = 0`. The warning message is just telling you that your executable doesn't have an entry point, so we can easily make one that does nothing. – Brian Berns Oct 28 '22 at 02:55