Как построить интеграционные тесты для приложения Saturn - PullRequest
2 голосов
/ 01 июня 2019

Я пытаюсь создать несколько интеграционных тестов для небольшого API, созданного мной с использованием Saturn framework .

API построен с обычными Saturn выражениями вычислений, такими как application, controller, router и т. Д.

Но чтобы построить интеграционные тесты, мне нужно заменить вычислительное выражение application (ce) и вручную WebHostBuilder.

Мой application се выглядит так:

module Server 

let app =
    application {
        url ("http://0.0.0.0:" + port.ToString() + "/")
        use_router apiRouter
        memory_cache
        service_config configureSerialization
        use_gzip
        use_config (fun _ ->
            System.Environment.CurrentDirectory <- (System.Reflection.Assembly.GetExecutingAssembly()).Location
                                                   |> Path.GetDirectoryName
            let configurationRoot =
                ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appSettings.json")
                    .Build()
            let appConfig = FsConfig.AppConfig(configurationRoot)

            let dbPath =
                match appConfig.Get<AppSettings>() with
                | Ok settings when settings.DbConfig.Database.Contains(":memory:") -> settings.DbConfig.Database
                | Ok settings -> Path.Combine(System.Environment.CurrentDirectory, settings.DbConfig.Database)
                | Error _ -> failwithf "Invalid database path"
            { connectionString = dbPath |> sprintf "DataSource=%s;Version=3" })
    } 

С router и одним из controllers ...

let cartController =
    controller {
        create createCartAction
        show getCartAction
        delete deleteCartAction
        subController "/items" cartItemsController
    }

let apiRouter =
    router {
        not_found_handler (setStatusCode 404 >=> text "Not found")
        pipe_through apiPipeline
        forward "/cart" cartController
    }

Код выше находится в моем проекте API, код с интеграционным тестом ниже - во втором проекте. Последний имеет ссылку на проект первого.

let configureApp (app : IApplicationBuilder) =
    app.UseGiraffe(Server.apiRouter) \\<-- The problem is here. Server.apiRouter is null!

let configureServices (services : IServiceCollection) =
    services.AddGiraffe() |> ignore

let builder = WebHostBuilder()
                .UseContentRoot(contentRoot) 
                .Configure(Action<IApplicationBuilder> configureApp)
                .ConfigureServices(configureServices)

let testServer = new TestServer(builder)

let client = testServer.CreateClient()

let! response = client.GetAsync "/"

test <@ HttpStatusCode.OK = response.StatusCode @> 

При запуске теста он завершается с исключением:

System.InvalidOperationException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'A suitable constructor for type 'Giraffe.Middleware+GiraffeMiddleware' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.' 

Кажется, проблема в линии app.UseGiraffe(Server.apiRouter). apiRouter определяется в модуле Server проекта API, но когда этот код выполняется в тестовом проекте, Server.apiRouter равен null.

Если, однако, я перенесу тестовый код в тот же проект, что и API - тест работает отлично.

Почему вычислительное выражение apiRouter будет null, если оно вызывается из тестового проекта?

1 Ответ

0 голосов
/ 23 июня 2019

Оказалось, что это никак не связано с Saturn. Это все связано с тем, как F# инициирует modules. Ответ можно найти здесь

Как предложено в ссылочном посте. Я просто добавил [<EntryPoint>] функцию, и все заработало как положено.

...