【问题标题】:Unit Testing with AOP/PostSharp使用 AOP/PostSharp 进行单元测试
【发布时间】:2014-03-25 13:32:32
【问题描述】:

我正在尝试使用 PostSharp 来实现安全方面,以便在我的存储库层中应用方法级别的授权检查。

概念概述here

然而,这些授权检查在单元测试过程中受到阻碍,使它们更多地进入集成测试。

将这些隔离为单元测试的最佳方法是什么,基本上忽略/模拟安全方面,这样我就可以测试实际的类行为而无需初始化一堆安全信息?

AOP 是否与单元测试有内在冲突?

【问题讨论】:

  • official PostSharp document that Gael linked to 实际上回答了您的第二个问题。该文档指出:“方面的测试技术不同于普通类库的测试技术”。换句话说:是的,PostSharp 与单元测试冲突。请注意,AOP 本身与单元测试冲突,但编译时编织会。拦截、修饰等 AOP 技术与单元测试不冲突。
  • @Steven 我同意。现在对 PostSharp 和 AOP 有了更好的理解,我可以看到前者的 AOP 方法需要不同的单元测试方法。

标签: c# unit-testing aop postsharp


【解决方案1】:

首先要回答您的第二个问题,不,AOP 本质上与单元测试并不冲突。通常我会说最好分别对方法和方面进行单元测试。

在你的情况下,有几个选择。

最简单的方法就是让单元测试设置方法确保线程具有所需的权限。

如果您不想这样做,有两种方法可以将单元测试分开。 首先是从您应用安全方面的方法中提取所有代码到单独的方法中,如下所示:

[SecurityAspect]
void DoSomething()
{
    DoSomethingInternal();
}

void DoSomethingInternal()
{
    // this is the real code
}

然后,您可以针对所有不安全的“内部”方法运行单元测试——测试其中的逻辑而不用担心安全性。

第二种方法是将模拟权限测试器注入方面本身。为了能够做到这一点,您必须定义一个单独的类和接口来执行测试安全性的实际逻辑,如下所示(假设它是您传入的一个线程来验证安全性):

public interface IPermissionsChecker
{
    bool HasPermissions(Thread thread);
}

这是您实时系统的权限检查器:

public class RealPermissionsChecker : IPermissionsChecker
{

    public bool HasPermissions(Thread thread)
    {
        // do your real work here
    }
}

这是您将在单元测试中使用的那个

public class MockPermissionsChecker : IPermissionsChecker
{
    public bool HasPermissions(Thread thread)
    {
        return true;
    }
}

现在你需要像这样定义你的方面:

public class SecurityChecker : OnMethodBoundaryAspect
{

    IPermissionsChecker _checker;

    public override void OnEntry(MethodExecutionArgs args) 
    { 
        if (!_checker.HasPermissions(Thread.CurrentThread))
            throw new SecurityException("No permissions");
    }
}

剩下的唯一问题是需要将正确的权限检查器注入方面。

我之前做的有点hacky的方法是让_checker成为一个静态字段,并提供一个静态方法来初始化它:

public class SecurityChecker : OnMethodBoundaryAspect
{

    private static IPermissionsChecker _checker;

    public static void InjectChecker(IPermissionsChecker checker)
    {
        // best put some code here to make sure this is only called once,
        // as well as doing thread synchronization
        if (_checker == null)
            _checker = checker;
    }

InjectChecker 是静态的这一事实意味着您可以从应用启动(或单元测试启动)代码中访问它。我怀疑单元测试纯粹主义者对此会皱眉头——你必须确保在应用程序启动时调用它,但我认为这是将检查器注入方面的最简单方法,避免了你的其余代码无法直接访问方面的实例。

更复杂的替代方法是在您的方面覆盖 RunTimeInitialize() - 此方法由 PostSharp 在方面初始化时调用。你可能会这样做:

    public override void RuntimeInitialize(MethodBase method)
    {
        base.RuntimeInitialize();
        this._checker =PermissionsCheckerProvider.Current.GetChecker();
    }

你会看到这需要你定义另一个类:

public class PermissionsCheckerProvider
{
    // make sure you set this at app startup, either to the mock or to the real checker
    public static PermissionsCheckerProvider Current { get; set;}

    public IPermissionsChecker GetChecker()
    {
    }
}

这种方法保证该方法将在正确的时间尝试其初始化,但是您会遇到一个问题,即确保在方面尝试初始化之前提供了适当的当前提供程序。所以我个人可能会选择第一种方法来保持简单。

这里有一些关于依赖注入和 RuntimeInitialize 的讨论。 https://codereview.stackexchange.com/questions/20341/inject-dependency-into-postsharp-aspect

【讨论】:

  • 我喜欢您能够注入权限检查器的第二个解决方案,我会跟进。同时,如果您有机会填写剩下的问题,我将不胜感激。
  • OK 我现在添加了有关如何注入权限检查器的更多信息。希望有帮助
【解决方案2】:

两个链接可以广泛回答您的问题:

【讨论】:

  • 感谢 Gael,网络研讨会非常有帮助。
【解决方案3】:

如果你在单元测试中使用 Typemock,你可以使用类似

MyAspect myAspectMock = Isolate.Fake.Instance<MyAspect>(Members.MustSpecifyReturnValues);
Isolate.Swap.AllInstances<MyAspect>().With(myAspectMock);

这使您可以控制使用方面的测试以及不使用方面的测试,从而允许您测试方法本身并应用建议。

大概会有与其他模拟框架类似的机制

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多