Skip to content

Instantly share code, notes, and snippets.

@illicitonion
Created October 7, 2011 21:46
Show Gist options
  • Select an option

  • Save illicitonion/1271421 to your computer and use it in GitHub Desktop.

Select an option

Save illicitonion/1271421 to your computer and use it in GitHub Desktop.
Quick proof of concept: Magic dynamic proxies for magic asserts
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