如何将DI框架中的AOP与C#TestFrameworks结合使用?(不适用于测试项目)

如何将DI框架中的AOP与C#TestFrameworks结合使用?(不适用于测试项目),c#,dependency-injection,nunit,aop,mbunit,C#,Dependency Injection,Nunit,Aop,Mbunit,我为一个糟糕的标题提前道歉-欢迎建议 我一直在读关于DI和AOP的书,我想我掌握了基本知识;至少对于添加日志记录的规范示例是这样的 我想将此应用于我们在NUnit中创建的测试用例,例如,能够自动为所有测试用例方法和它们调用的任何“助手方法”添加入口/出口日志记录。(我与NUnit没有关系——如果在另一个框架中更容易,请让我知道。) 注-这与受试者无关;我想将这些技术应用于测试用例本身 这是非常清楚的如何做到这一点使用-这是他们的,但我不想添加他们的许可证处理到我们的项目只是为了这个实验 我在AO

我为一个糟糕的标题提前道歉-欢迎建议

我一直在读关于DI和AOP的书,我想我掌握了基本知识;至少对于添加日志记录的规范示例是这样的

我想将此应用于我们在NUnit中创建的测试用例,例如,能够自动为所有测试用例方法和它们调用的任何“助手方法”添加入口/出口日志记录。(我与NUnit没有关系——如果在另一个框架中更容易,请让我知道。)

注-这与受试者无关;我想将这些技术应用于测试用例本身

这是非常清楚的如何做到这一点使用-这是他们的,但我不想添加他们的许可证处理到我们的项目只是为了这个实验

我在AOP for C#中找到的所有其他参考都是基于IoC容器实现提供的(动态)拦截器,如CastleWindsor、Unity、Spring.Net等等。。。在这种情况下,它们都有一个共同的问题:您需要一段设置代码来为要添加拦截器的对象创建代理。(我原本认为这段代码还必须创建一个IoC容器,但我发现我错了。)

但是我看不出nUnit测试用例的设置代码会用到哪里

我提出的选项及其问题:

  • 让testfixture类构造函数为自己创建一个代理。由于递归,无法工作(消费者请求对象,对象尝试将代理返回到对象,代理尝试从读取创建对象)
  • 使用我自己的基于反射的魔法(这对我来说是一项巨大的任务)
  • 让构造函数包装testfixture类中的所有方法并返回此“包装”对象(不确定构造函数是否可以这样做)
  • 在testfixture上使用静态构造函数来实现这一神奇功能(假设您可以将类的方法动态包装到位)
  • 使用模块cctor(via)在模块级执行一些操作,并使用日志将所有类中的所有方法包装起来
  • 最简单的是:某种用于实例化测试用例的工厂(不是测试的参数),从中我可以使用一个IoC代理生成器
  • 对于nUnit来说:我能找到的唯一方法就是创建一个。优势-可能不会中断与ReSharper的集成。缺点-部署到所有开发人员机器上,尤其是在NUnit更新时。有没有其他方法可以为nUnit做到这一点
  • 对于MbUnit:看起来是这样,这是直截了当的。优点:易于部署到所有开发人员。缺点:测试不会出现在Resharper中。旁注:
  • 我的选择和结论中是否遗漏了什么


    有没有我错过的更简单的方法呢?

    面向方面编程不仅仅是使用动态代理(拦截)或编译后代码编织(PostSharp)。AOP主要是关于添加横切关注点。使用动态代理是添加横切关注点的一种方法。代码编织是实现这一点的另一种方法。但还有另一种更好的方式来增加交叉关注点

    不要使用动态代理或代码编织,而是让应用程序的设计引导您。当您使用正确的抽象设计应用程序时,使用decorator添加横切关注点是很容易的。您可以找到使用适当的抽象和设计的系统示例

    这些文章描述了如何使用装饰器定义横切关注点。以这种方式设计系统时,可以从代码的其余部分中分别测试横切关注点的实现。当使用正确的抽象时,这将很容易

    当您这样做时,不需要在单元测试中做任何特殊的事情。不需要代码编织,也不需要在测试中运行DI容器来为您构建对象。您可以测试您的应用程序逻辑,而无需任何横切问题。您可以单独测试每个小部件,并将所有部件放在应用程序的中。

    这里是一个使用(通过在测试项目上安装+numets)的示例

    使用系统;
    使用System.Collections.Generic;
    使用System.Linq;
    运用系统反思;
    使用System.Threading.Tasks;
    使用NUnit.Framework;
    使用Puresharp;
    名称空间测试
    {
    /// 
    ///类以使用NUnit进行测试
    /// 
    公共类计算器
    {
    公共整数相加(整数a、整数b)
    {
    返回a+b;
    }
    }
    /// 
    ///一个简单的计算器测试类。
    /// 
    [测试夹具]
    公共类计算器测试
    {
    /// 
    ///将方面附加到测试方法的静态构造函数(类构造函数)。
    /// 
    静态计算器测试()
    {
    //实例化我的自定义方面。
    var myBasicAspect=新的mucustomspect();
    //将自定义特性附加到自定义切入点
    myBasicAspect.Weave();
    }
    [测试]
    public void应添加两个数字()
    {
    var_calculator=新计算器();
    int _result=_calculator.Add(2,8);
    断言(_result,Is.EqualTo(10));
    }
    }
    /// 
    ///切入点用于确定要编织的方法组(此处为CalculatorTest的测试方法)。
    /// 
    公共类MyCustomPointcut:切入点
    {
    重写公共布尔匹配(MethodBase方法)
    {
    return method.DeclaringType==typeof(CalculatorTest)和&method.GetCustomAttributes(typeof(TestAttribute),true).Any();
    }
    }
    /// 
    ///这是一个很好的方面。
    /// 
    公共类mucustomspect:方面
    {
    公共重写IEnumerable管理(MethodBase方法)
    {
    //Aspect将使用MyCustomAdvice在边界上通知方法。
    收益率
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Threading.Tasks;
    using NUnit.Framework;
    using Puresharp;
    
    namespace TEST
    {
        /// <summary>
        /// Class to test with NUnit
        /// </summary>
        public class Calculator
        {
            public int Add(int a, int b)
            {
                return a + b;
            }
        }
    
        /// <summary>
        /// Test class for calculator with a simple test.
        /// </summary>
        [TestFixture]
        public class CalculatorTest
        {
            /// <summary>
            /// Static constructor (class constructor) to attach aspect to test method.
            /// </summary>
            static CalculatorTest()
            {
                //Instantiate my custom aspect.
                var myBasicAspect = new MuCustomAspect();
    
                //Attach my custom aspect to my custom pointcut
                myBasicAspect.Weave<MyCustomPointcut>();
            }
    
            [Test]
            public void ShouldAddTwoNumbers()
            {
                var _calculator = new Calculator();
                int _result = _calculator.Add(2, 8);
                Assert.That(_result, Is.EqualTo(10));
            }
        }
    
        /// <summary>
        /// Pointcut to identify methods group to weave (here test methods of CalculatorTest).
        /// </summary>
        public class MyCustomPointcut : Pointcut
        {
            override public bool Match(MethodBase method)
            {
                return method.DeclaringType == typeof(CalculatorTest) && method.GetCustomAttributes(typeof(TestAttribute), true).Any();
            }
        }
    
        /// <summary>
        /// Défine an aspect.
        /// </summary>
        public class MuCustomAspect : Aspect
        {
            public override IEnumerable<Advisor> Manage(MethodBase method)
            {
                //Aspect will advice method on boundary using MyCustomAdvice.
                yield return Advice.For(method).Around(() => new MyCustomAdvice());
            }
        }
    
        /// <summary>
        /// Define an advice.
        /// </summary>
        public class MyCustomAdvice : IAdvice
        {
            public MyCustomAdvice()
            {
            }
    
            public void Instance<T>(T value)
            {
            }
    
            public void Argument<T>(ref T value)
            {
            }
    
            public void Begin()
            {
            }
    
            public void Await(MethodInfo method, Task task)
            {
            }
    
            public void Await<T>(MethodInfo method, Task<T> task)
            {
            }
    
            public void Continue()
            {
            }
    
            public void Return()
            {
            }
    
            public void Return<T>(ref T value)
            {
            }
    
            public void Throw(ref Exception exception)
            {
            }
    
            public void Throw<T>(ref Exception exception, ref T value)
            {
            }
    
            public void Dispose()
            {
            }
        }
    }