1. 程式人生 > 實用技巧 >Orchard Core 中執行帶程式上下文的單元測試

Orchard Core 中執行帶程式上下文的單元測試

Orchard Core 帶有很多單元測試,使用 Xunit 單元測試框架,除了簡單的直接呼叫待測試的方法,有一些複雜的測試是需要上下文的,甚至需要 Application 程式啟動起來,Orchard Core 的例子中有一個基於 HTTP 的 Application 測試,但是其測試都是通過呼叫 HTTP API 執行的,測試 Controller 挺方便,但是測試 Service 等就麻煩了,而且測試往往是需要呼叫內部的一些方法的,所以 HTTP API 測試適用範圍有限。

`WebApplicationFactory` can only test by http method, could not direct call Application's component, like do some Session.Query() then direct call some method to do a complex test.

所以自己做了個能夠啟動 Application 且在Application 上下文內執行測試的單元測試基類和輔助方法。使用方便,繼承即可使用,然後你就可以像在 Orchard Core 內部寫程式碼一樣,去呼叫各種 Service、Query 進行測試啦。

由於是從我給 Orchard Core 團隊提的 issue 裡面整理拷貝而來,中英文混合,將就著看,主要把我的實現程式碼分享,方便有需要的人。

 public class AppTestBase<TFixture> : IClassFixture<TFixture> where TFixture : class
, IApplicationStartupFixture
{
public readonly TFixture Application; public AppTestBase(TFixture application) { this.Application = application; } protected T GetService<T>() where T : class { return this.Application.ServiceProvider.GetService<T>(); }
protected T GetRequiredService<T>() where T : class { return this.Application.ServiceProvider.GetRequiredService<T>(); } protected async Task RunInShellScopeAsync(Func<ShellScope, Task> execute) { var shellContextFactory = GetRequiredService<IShellContextFactory>(); IShellHost shellHost = GetRequiredService<IShellHost>(); using (var shell = await shellContextFactory.CreateDefaultShellContext()) using (var shellScope = shell.CreateScope()) { var httpContextAccessor = shellScope.ServiceProvider.GetService<IHttpContextAccessor>(); httpContextAccessor.HttpContext = shellScope.ShellContext.CreateHttpContext(); await shellScope.UsingAsync(execute); } } protected T CreateController<T>(ShellScope shellScope, HttpContext httpContext) { var controllerActivatorProvider = shellScope.ServiceProvider.GetService<IControllerActivatorProvider>(); var controllerContext = new ControllerContext() { HttpContext = httpContext, }; var controllerObj = (T) controllerActivatorProvider.CreateActivator( new ControllerActionDescriptor() {ControllerTypeInfo = (TypeInfo) typeof(T),}) .Invoke(controllerContext); return controllerObj; } }

add database setting in ShellSettings (required):

    settings["DatabaseProvider"] ="SqlConnection";
    settings["ConnectionString"] = "Server=localhost;Database=xxx;User Id=sa;Password=xxxxxxx";
    settings["TablePrefix"] = "xxx";

    
public static class TestShellContextFactoryExtensions
{
    internal static Task<ShellContext> CreateDefaultShellContext(this IShellContextFactory shellContextFactory)
    {
            var settings = new ShellSettings()
            {
                Name = ShellHelper.DefaultShellName,
                State = TenantState.Running
            };
            
        settings["DatabaseProvider"] ="SqlConnection";
        settings["ConnectionString"] = "Server=localhost;Database=xxx;User Id=sa;Password=xxxxxxx";
        settings["TablePrefix"] = "xxx";
    
            return shellContextFactory.CreateDescribedContextAsync(settings, new ShellDescriptor()
            {
                Features = new List<ShellFeature>()
                {
                    new ShellFeature("Application.Default"),
                    new ShellFeature("OrchardCore.Setup"),
                    new ShellFeature("Operational"),
                },
                Parameters = new List<ShellParameter>(),
            });
            // return shellContextFactory.CreateShellContextAsync(settings);
    }

}

An helper to create `HttpContext`, and set set `shell.RequestServices` to `HttpContext`.

    public static HttpContext CreateHttpContext(this ShellContext shell)
    {
        var settings = shell.Settings;

        var context = new DefaultHttpContext();
         
                // set shell.RequestServices to context
        context.RequestServices = shell.ServiceProvider;
        OrchardCore.Modules.HttpContextExtensions.UseShellScopeServices(context);

        var urlHost = settings.RequestUrlHost?.Split('/',
            StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();

        context.Request.Host = new HostString(urlHost ?? "localhost");

        if (!String.IsNullOrWhiteSpace(settings.RequestUrlPrefix))
        {
            context.Request.PathBase = "/" + settings.RequestUrlPrefix;
        }

        context.Request.Path = "/";
        context.Items["IsBackground"] = true;

        context.Features.Set(new ShellContextFeature
        {
            ShellContext = shell,
            OriginalPathBase = String.Empty,
            OriginalPath = "/"
        });

        return context;
    }


使用的例子(先繼承基類):

    [Fact]
    public async Task InAppRuntimeTest() {
        await RunInShellScopeAsync(async shellScope =>
        {
            var session = shellScope.ServiceProvider.GetService<ISession>();
            Assert.NotNull(session);

            var controllerObj =
                    CreateController<XxxxxController>(shellScope, shellScope.ShellContext.CreateHttpContext());

            var result = await controllerObj.Index(new XxxxModel(){});

            Assert.NotNull(result);
        });
    }