How to use AOP from DI Frameworks With C# TestFrameworks? (NOT for the item under test)

我与影子孤独终老i 提交于 2019-12-05 13:37:33

Aspect Oriented Programming is not only about using dynamic proxies (interception) or post compilation code weaving (PostSharp). AOP is mainly about adding cross-cutting concerns. Using dynamic proxies is one way of adding cross-cutting concerns. Code weaving is another way of doing this. But there is another, IMO better, way of adding cross-cutting concerns.

Instead of using dynamic proxies or code weaving, let the design of your application lead you. When you design your application using the right abstractions, it would be easy to add cross-cutting concerns using decorators. You can find examples of systems that are designed using proper abstractions here and here.

Those articles describe how you define cross-cutting concerns using decorators. When you designed the system in this way, you can test the implementation of a cross-cutting concern separately from the rest of the code. This will be easy when using the right abstractions.

When you do this, there is no need to do anything special in your unit tests. No need for code weaving, no need to run your DI container in your test to build up objects for you. You can test your application logic without any cross-cutting concern to be in the way. You can test every little piece in isolation and put all pieces together it in your application’s the Composition Root.

Here a sample using Puresharp Framework (by installing IPuresharp + Puresharp nugets on test project.)

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()
        {
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!