使用PerWebRequest生命周期测试Castle Windsor组件

19

我正试图进行与Castle Windsor有关的测试,其中一个测试中我想要检查Windsor安装程序,因此我检查容器能否根据其接口解析组件。

到目前为止还好,问题出在组件在其安装程序中具有PerWebRequest生命周期时,一开始它抱怨HttpContext.Current为空,通过在测试设置中创建一个虚假上下文来解决这个问题后,现在我在nunit测试中遇到了这个异常

System.Exception:看起来您忘记注册http模块Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule,请将''添加到web.config的

部分。如果您正在运行集成模式下的IIS7,则需要将其添加到

部分下的

部分。由于我是从NUnit运行此任务,因此我该如何在Windsor中注册模块或类才能使其正常工作,或者如何进行Mock,因为在此测试中确实不是Web请求,只是检查容器是否可以解析该类型。

如果我在实际Web请求之外对此组件进行任何集成测试,也会发生同样的情况,有没有办法让这个工作或真正Mock一个Web请求以便运行这些测试?

提前感谢

Fer


1
我昨天在https://dev59.com/kW025IYBdhLWcg3w1JiG上也问过类似的问题。 - KallDrexx
4个回答

21
在您的测试中,您可以订阅ComponentModelCreated事件并将每个Web请求组件的生命周期更改为其他内容(示例)。
如果您正在编写范围为单个请求的集成测试,则应使用singleton。
如果您正在编写跨多个请求的集成测试,则可以使用上下文生命周期来模拟请求的范围。
编辑:包括示例代码(不再可用):
container.Kernel.ComponentModelCreated += Kernel_ComponentModelCreated;

void Kernel_ComponentModelCreated(Castle.Core.ComponentModel model)
{
    if (model.LifestyleType == LifestyleType.Undefined)
        model.LifestyleType = LifestyleType.Transient;
}

2
Mauricio,非常感谢您的回答,我已经为此苦思冥想了一段时间。对于Windsor还比较新手。 - Fernando Salas
谢谢你的想法。需要按预期对覆盖功能进行单元测试。通过两次解析相同的IType并检查对象是否相同,已经完成了LifestyleSingleton的单元测试。 - Alex M
2
它可以工作,但我必须检查PerWebRequest而不是Undefined的生命周期。也许这是自您几年前发布答案以来行为发生变化。因此,条件变为:if (model.LifestyleType == Castle.Core.LifestyleType.PerWebRequest)。谢谢! - bN_

1
从Windsor的第5个版本开始,如果您正在使用Castle.Facilities.AspNet.SystemWeb.WebRequestScopeAccessor,则接受的答案将不起作用,因为PerWebRequest生命周期已经是一个作用域生命周期。
我通过将ComponentModelCreated委托更改为以下内容使其工作:
void Kernel_ComponentModelCreated(Castle.Core.ComponentModel model)
{
    const string CastleScopeAccessorType = "castle.scope-accessor-type";

    if (model.ExtendedProperties.Contains(CastleScopeAccessorType))
    {
        model.ExtendedProperties.Remove(CastleScopeAccessorType);
    }
}

1
我最终实现了这个扩展。注意: 必须在使用 PerWebRequest 生命周期的组件加载之前调用此扩展:
public static class WindsorContainerExtensions
{
    public static IWindsorContainer OverridePerWebRequestLifestyle(this IWindsorContainer container)
    {
        container.Kernel.ComponentModelCreated += model =>
        {
            if (model.IsPerWebRequestLifestyle())
            {
                model.LifestyleType = LifestyleType.Transient;
            }
        };

        return container;
    }

    private static bool IsPerWebRequestLifestyle(this ComponentModel model)
    {
        return model.LifestyleType == LifestyleType.Scoped
            && model.HasAccessorType(typeof(WebRequestScopeAccessor));
    }

    private static bool HasAccessorType(this ComponentModel model, Type type)
        => model.HasExtendedProperty("castle.scope-accessor-type", type);

    private static bool HasExtendedProperty<T>(this ComponentModel model, object key, T expected)
    {
        return model.ExtendedProperties[key] is T actual
            && EqualityComparer<T>.Default.Equals(actual, expected);
    }
}

需要这些导入库:

using System;
using System.Collections.Generic;
using Castle.Core;
using Castle.Facilities.AspNet.SystemWeb;
using Castle.Windsor;

0
如果您还想检查作用域类型是否为每个 Web 请求,您也可以这样做。
var isPerWebRequestScope = JsonConvert.SerializeObject(model.ExtendedProperties).Contains("Castle.Facilities.AspNet.SystemWeb.WebRequestScopeAccessor")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接