Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebApplicationFactory<Program> can only be used with workaround in F# aspnet integration tests #18302

Open
jkone27 opened this issue Feb 9, 2025 · 3 comments

Comments

@jkone27
Copy link
Contributor

jkone27 commented Feb 9, 2025

the only way to make use of integration tests for aspnetcore from F# code , is either use the old Startup class setup (not valid for minimal api), or use this setup which makes use of a "trick" of declaring both a type and a module right after for Program as else the Program static class is not visible for unit/integration tests in the minimal api form?

Image

then it can be used by WebApplicationFactory<Program> in F# unit tests

@jkone27
Copy link
Contributor Author

jkone27 commented Feb 9, 2025

closing as i need to verify something else, sorry

@jkone27
Copy link
Contributor Author

jkone27 commented Feb 9, 2025

ok checked and this also doesnt work it seems or at least can be confusing for new users...

<ItemGroup>
     <InternalsVisibleTo Include="MyTestProject" />
</ItemGroup>

in F#, so probably we can reopen this one?

@jkone27 jkone27 reopened this Feb 9, 2025
@brianrourkeboll
Copy link
Contributor

Yeah, it's because WebApplicationFactory requires that Program not be a static class, but the implicit or explicit Program module in F# is compiled as a static class.

Another way you can work around it—which I think is conceptually simpler anyway—is to simply expose a public higher-order app function that accepts a builder configuration function and returns the built web app.

In Program.fs, just call the app function and pass in ignore (or (fun _ -> ())), then call .Run () on the web app just like normal:

Program.fs

module Program

open System
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.Hosting

let app configureBuilder =
    let builder = WebApplication.CreateBuilder (Environment.GetCommandLineArgs ())

    // Your normal setup logic goes here, before the call to configureBuilder.
    ignore <| builder.Logging.AddConsole ()
    ignore <| builder.Services.AddSingleton<SomeService1> ()
    ignore <| builder.Services.AddSingleton<SomeService2> ()
    // …etc.

    ignore <| configureBuilder builder

    let app = builder.Build ()
    ignore <| app.MapGet ("/", Func<_> (fun () -> "Hello, world"))
    app

(app ignore).Run ()

In your tests, you can just pass in a function that configures the builder however you want, overriding services (or not), etc., as needed:

Tests.fs

let myTest route x y z =
    task {
        use app = Program.app (fun builder ->
            ignore <| builder.WebHost.UseTestServer ()
            // ignore <| builder.Services. … whatever else you want to override before `builder.Build ()` is called.
         )

        do! app.StartAsync ()
        use client = app.GetTestClient ()
    
        // The rest of your test logic.
        use! resp = client.GetAsync route
        // Your assertions, etc…
    }

Here's another example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

2 participants