C# 使用AOP/PostSharp进行单元测试
我正试图使用PostSharp实现一个安全方面,以便在我的存储库层中应用方法级授权检查 概述了这一概念 然而,这些授权检查在单元测试过程中遇到了阻碍,使它们更多地参与到集成测试中 将这些隔离为单元测试的最佳方法是什么,基本上忽略/模拟安全方面,这样我就可以只测试实际的类行为,而无需初始化一堆安全信息C# 使用AOP/PostSharp进行单元测试,c#,unit-testing,aop,postsharp,C#,Unit Testing,Aop,Postsharp,我正试图使用PostSharp实现一个安全方面,以便在我的存储库层中应用方法级授权检查 概述了这一概念 然而,这些授权检查在单元测试过程中遇到了阻碍,使它们更多地参与到集成测试中 将这些隔离为单元测试的最佳方法是什么,基本上忽略/模拟安全方面,这样我就可以只测试实际的类行为,而无需初始化一堆安全信息 AOP本质上与单元测试冲突吗?首先回答您的第二个问题,不,AOP本质上与单元测试冲突。通常我会说最好分别对方法和方面进行单元测试 在您的情况下,有几种选择 最简单的方法就是让单元测试设置方法确保线程
AOP本质上与单元测试冲突吗?首先回答您的第二个问题,不,AOP本质上与单元测试冲突。通常我会说最好分别对方法和方面进行单元测试 在您的情况下,有几种选择 最简单的方法就是让单元测试设置方法确保线程具有所需的权限 如果您不想这样做,有两种方法可以为您的单元测试分离内容。 第一种方法是将应用安全方面的方法中的所有代码提取到单独的方法中,如下所示:
[SecurityAspect]
void DoSomething()
{
DoSomethingInternal();
}
void DoSomethingInternal()
{
// this is the real code
}
public class SecurityChecker : OnMethodBoundaryAspect
{
IPermissionsChecker _checker;
public override void OnEntry(MethodExecutionArgs args)
{
if (!_checker.HasPermissions(Thread.CurrentThread))
throw new SecurityException("No permissions");
}
}
public override void RuntimeInitialize(MethodBase method)
{
base.RuntimeInitialize();
this._checker =PermissionsCheckerProvider.Current.GetChecker();
}
然后,您可以针对所有不安全的“内部”方法运行单元测试——这些方法可以测试其中的逻辑,而不必担心安全性
第二种方法是将模拟权限测试人员注入方面本身。要做到这一点,您必须定义一个单独的类和接口来执行安全性测试的实际逻辑,类似于这样(假设您传入一个线程来验证安全性):
这是您的实时系统的权限检查器:
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;
}
}
现在您需要像这样定义您的方面:
[SecurityAspect]
void DoSomething()
{
DoSomethingInternal();
}
void DoSomethingInternal()
{
// this is the real code
}
public class SecurityChecker : OnMethodBoundaryAspect
{
IPermissionsChecker _checker;
public override void OnEntry(MethodExecutionArgs args)
{
if (!_checker.HasPermissions(Thread.CurrentThread))
throw new SecurityException("No permissions");
}
}
public override void RuntimeInitialize(MethodBase method)
{
base.RuntimeInitialize();
this._checker =PermissionsCheckerProvider.Current.GetChecker();
}
剩下的唯一问题是需要将正确的权限检查器注入方面
我以前做过的一种有点粗俗的方法是将_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调用此方法。你可能会这样做:
[SecurityAspect]
void DoSomething()
{
DoSomethingInternal();
}
void DoSomethingInternal()
{
// this is the real code
}
public class SecurityChecker : OnMethodBoundaryAspect
{
IPermissionsChecker _checker;
public override void OnEntry(MethodExecutionArgs args)
{
if (!_checker.HasPermissions(Thread.CurrentThread))
throw new SecurityException("No permissions");
}
}
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()
{
}
}
这种方法保证该方法将在正确的时间尝试初始化,但是在方面尝试初始化之前,您有一个问题,即确保您提供了适当的当前提供程序。所以我个人可能会选择第一种方法来保持简单
这里有一些关于依赖项注入和运行时初始化的讨论 两个全面回答您问题的链接:
- 有两种不同的观点:马特·格罗夫斯和我自己
- 关于方面的测试
MyAspect myAspectMock = Isolate.Fake.Instance<MyAspect>(Members.MustSpecifyReturnValues);
Isolate.Swap.AllInstances<MyAspect>().With(myAspectMock);
MyAspect myAspectMock=Isolate.Fake.Instance(Members.mustSpecificReturnValues);
使用(myAspectMock)隔离.Swap.AllInstances();
这允许您控制在哪些测试上使用方面,哪些不使用方面,允许您测试方法本身,并应用建议
其他模拟框架可能也会有类似的机制我喜欢您的第二个解决方案,即能够注入权限检查器,我将跟进。同时,如果您有机会填写剩下的问题,我将非常感激。好的,我现在添加了有关如何注入权限检查器的更多信息。希望对你有帮助汉克斯·盖尔,网络研讨会非常有帮助。它实际上回答了你的第二个问题。文档说明:“方面的测试技术不同于普通类库的测试技术”。换句话说:是的,PostSharp与单元测试冲突。请注意,AOP本身并不与单元测试冲突,但编译时编织会与之冲突。AOP技术,如拦截和修饰,与单元测试并不冲突。@Steven我同意。现在对PostSharp和AOP有了更全面的了解,我可以看出,前者的AOP方法需要不同的单元测试方法。PostSharp需要不同的单元测试方法(因为它进行编译时编织),但AOP本身并不需要(因为AOP不规定是在编译时还是在运行时添加方面)。