Created
October 7, 2011 21:46
-
-
Save illicitonion/1271421 to your computer and use it in GitHub Desktop.
Quick proof of concept: Magic dynamic proxies for magic asserts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Dynamic; | |
| using System.Reflection; | |
| using Microsoft.CSharp.RuntimeBinder; | |
| using NUnit.Framework; | |
| namespace MagicDynamicTests | |
| { | |
| [TestFixture] | |
| public class DynamicTest | |
| { | |
| [Test] | |
| public void AssertSausageBasketCostsSameAsOneSausage() | |
| { | |
| Basket basket = new Basket {Cost = 8}; | |
| // The proxy (stored in the dynamic field Assert) will take the names of the named arguments to look up | |
| // an Assert method on the test fixture with a name related to the argument names, by convention. | |
| // Beautiful Assert line. Shame about all the magic. | |
| Assert(basket, Costs: 7); | |
| } | |
| private dynamic Assert; | |
| public DynamicTest() | |
| { | |
| Assert = new AssertProvider(this); | |
| } | |
| [Test] | |
| public void FishEqualsChipsPasses() | |
| { | |
| Assert(Fish: 2, Chips: 2); | |
| } | |
| [Test] | |
| public void FishNotEqualsChipsFails() | |
| { | |
| Assert(Fish: 2, Chips: 3); | |
| } | |
| [Test] | |
| public void SausageShouldNotBeEggs() | |
| { | |
| Assert(Sausage: 3, Eggs: 3); | |
| } | |
| public void AssertFishChips(int Fish, int Chips) | |
| { | |
| NUnit.Framework.Assert.AreEqual(Fish, Chips); | |
| } | |
| public void AssertSausageEggs(int Sausage, int Eggs) | |
| { | |
| NUnit.Framework.Assert.AreNotEqual(Sausage, Eggs); | |
| } | |
| public class Basket | |
| { | |
| public int Cost { get; set; } | |
| } | |
| public void AssertBasketCosts(Basket Basket, int Costs) | |
| { | |
| NUnit.Framework.Assert.AreEqual(Costs, Basket.Cost); | |
| } | |
| } | |
| public class AssertProvider : DynamicObject | |
| { | |
| private readonly object m_RealImplementor; | |
| public AssertProvider(object realImplementor) | |
| { | |
| m_RealImplementor = realImplementor; | |
| } | |
| public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) | |
| { | |
| string targetMethodName = "Assert"; | |
| var binderRealType = binder.GetType(); | |
| var realArguments = (List<CSharpArgumentInfo>)binderRealType.GetField("m_argumentInfo", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(binder); | |
| var arginfoRealType = typeof (CSharpArgumentInfo); | |
| var nameGetter = arginfoRealType.GetMethod("get_Name", BindingFlags.NonPublic | BindingFlags.Instance); | |
| for (int i = 1; i < realArguments.Count; i++) | |
| { | |
| string name = (string)nameGetter.Invoke(realArguments[i], new object[] { }); | |
| if (name != null) | |
| { | |
| targetMethodName += name; | |
| } | |
| else | |
| { | |
| targetMethodName += args[i - 1].GetType().Name; | |
| } | |
| } | |
| var methodInfo = m_RealImplementor.GetType().GetMethod(targetMethodName); | |
| try | |
| { | |
| methodInfo.Invoke(m_RealImplementor, args); | |
| } | |
| catch(TargetInvocationException e) | |
| { | |
| throw e.InnerException; | |
| } | |
| result = null; | |
| return true; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment