首先要回答您的第二个问题,不,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