An assert that will let you assert multiple things at the same time
AND gives good failed assert messages.
Instead of:
Assert.That(x != null);
Assert.That(x.Length > 3);
Assert.That(x[0] == "Foo");
You can write:
Spec.That(() => x != null
&& x.Length > 3
&& x[0] == "Foo");
Enjoy!
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
using NUnit.Framework;
namespace SvnTracker.Lang
{
/// <summary>
/// How to use:
///
/// Spec.That(()=> your assertion here);
/// E.g.
/// Spec.That(()=> 1 > 0);
///
/// or you could do:
///
/// 1.Assert(n => n > 0);
///
/// There is also an implementation of like which uses an object initialisation to
/// compare against an existing object, but only equality can be compared and worse,
/// only properties with setters which I think would lead people into bad habbits.
/// </summary>
public static class Spec//ify
{
public static bool Like<T>(this T subject, Expression<Func<T>> template)
{
ApplyLike("", subject, (MemberInitExpression) template.Body, true);
return true;
}
public static bool NotLike<T>(this T subject, Expression<Func<T>> template)
{
ApplyLike("", subject, (MemberInitExpression) template.Body, false);
return true;
}
class Results
{
public Results()
{
Output = new StringBuilder();
}
public bool Fail { get; set; }
public StringBuilder Output { get; private set; }
}
private static void ApplyLike(string level, object subject, MemberInitExpression template, bool success)
{
var results = new Results();
foreach (var binding in template.Bindings)
{
ApplyLike(level, subject, binding, success, results);
}
if (results.Fail)
{
throw new AssertionException("\r\n" + results.Output);
}
}
private static void ApplyLike(string level, object subject, MemberBinding binding, bool success, Results results)
{
var expression = ((MemberAssignment) binding).Expression;
object actual;
if (binding.Member is FieldInfo)
{
var field = (FieldInfo) binding.Member;
actual = field.GetValue(subject);
}
else
{
var prop = (PropertyInfo) binding.Member;
actual = prop.GetGetMethod().Invoke(subject, new object[] {});
}
if (expression.NodeType == ExpressionType.MemberInit)
{
var initExp = (MemberInitExpression) expression;
results.Output.Append(level + "\t" + binding.Member.Name + " = ");
results.Output.AppendLine(initExp.NewExpression.ToString());
foreach (MemberBinding childBinding in initExp.Bindings)
{
ApplyLike(level + "\t", actual, childBinding, success, results);
}
return;
}
if (expression is ListInitExpression)
{
var listExp = (ListInitExpression) expression;
if (actual is IList)
{
foreach (ElementInit element in listExp.Initializers)
{
Step value = new ExpressionEvaluator(null, null).Visit(element.Arguments[0]);
if (!((IList) actual).Contains(value.Value))
{
results.Fail = true;
results.Output.AppendLine(level + "\t" + binding.Member.Name + ": " + value.Value +
" not contained");
}
//Technically only need add + are enumerable
}
}
else
{
var actDict = (IDictionary) actual;
foreach (ElementInit element in listExp.Initializers)
{
Step key = new ExpressionEvaluator(null, null).Visit(element.Arguments[0]);
Step value = new ExpressionEvaluator(null, null).Visit(element.Arguments[1]);
if (!actDict.Contains(key.Value))
{
results.Fail = true;
results.Output.AppendLine(level + "\t" + binding.Member.Name + ": " + value.Value +
" not contained");
}
else
{
var entryValue = actDict[key.Value];
if (element.Arguments[1] is MemberInitExpression)
{
if (entryValue == null)
{
//We expect an object to be here.
results.Fail = true;
string message = binding.Member.Name + "[" + key.Value + "]";
results.Output.AppendLine(level + "\t" + message + " Expected: '" + value.Value +
"'");
results.Output.AppendLine(level + '\t' + Multiply(' ', message.Length) +
" Actual: '" + Display(entryValue) + "'");
}
else
{
var exp = (MemberInitExpression) element.Arguments[1];
string message = binding.Member.Name + "[" + key.Value + "]";
results.Output.AppendLine(level + "\t" + message);
foreach (MemberBinding childBinding in exp.Bindings)
{
ApplyLike(level + "\t", entryValue, childBinding, success, results);
}
}
}
else
{
var equal = ExpressionEvaluator.Eval<bool>(ExpressionType.Equal, value.Value,
entryValue);
//Check values are equal.
if (!equal)
{
results.Fail = true;
string message = binding.Member.Name + "[" + key.Value + "]";
results.Output.AppendLine(level + "\t" + message + " Expected: '" + value.Value +
"'");
results.Output.AppendLine(level + '\t' + Multiply(' ', message.Length) +
" Actual: '" + entryValue + "'");
}
}
}
}
}
return;
}
//Assume dict.
{
Step value = new ExpressionEvaluator(null, null).Visit(expression);
//field or property.
var equal = ExpressionEvaluator.Eval<bool>(ExpressionType.Equal, value.Value, actual);
if (!(equal == success))
{
results.Fail = true;
results.Output.AppendLine(level + "\t" + binding.Member.Name + " Expected: '" + value.Value + "'");
results.Output.AppendLine(level + '\t' + Multiply(' ', binding.Member.Name.Length) + " Actual: '" +
actual + "'");
}
}
return;
}
private static string Display(object value)
{
return value == null ? "null" : value.ToString();
}
private static string Multiply(char ch, int length)
{
var sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
sb.Append(ch);
return sb.ToString();
}
public static bool AssertFalse(Expression<Func<bool>> func)
{
Assert(false, func);
return true;
}
public static bool AssertFalse<T>(this T subject, Expression<Func<T, bool>> func)
{
Assert(subject, false, func);
return true;
}
public static bool That(Expression<Func<bool>> func)
{
Assert(true, func);
return true;
}
public static bool Assert<T>(this T subject, Expression<Func<T, bool>> func)
{
Assert(subject, true, func);
return true;
}
public static void Assert(bool success, Expression<Func<bool>> func)
{
Assert(success, success, null, func.Body);
}
private static void Assert<T>(this T subject, bool success, Expression<Func<T, bool>> func)
{
Assert(subject, success, func.Parameters[0], func.Body);
}
private static void Assert<T>(this T subject, bool success, ParameterExpression parameter, Expression expression)
{
var sb = new StringBuilder();
bool failed = false;
var exps = new List<Expression>();
Expand(expression, exps);
Exception lastCaught = null;
foreach (var exp in exps)
{
var sbb = new StringBuilder();
if (Eval(parameter, exp, subject, sbb, out lastCaught) != success)
{
failed = true;
sb.Append(sbb.ToString());
}
}
string msg = sb.ToString();
if (failed)
{
var message = "\r\n" + subject + " fails these constraints:\r\n\r\n" + msg;
if (lastCaught != null)
{
throw new AssertionException(message, lastCaught);
}
throw new AssertionException(message);
}
}
private static readonly Dictionary<ExpressionType, Func<Expression, UnaryExpression>> uniExps =
new Dictionary<ExpressionType, Func<Expression, UnaryExpression>>{
{ ExpressionType.Not, Expression.Not},
{ ExpressionType.Negate, Expression.Negate},
{ ExpressionType.NegateChecked, Expression.NegateChecked},
{ ExpressionType.UnaryPlus, Expression.UnaryPlus},
//{ ExpressionType.Quote, Expression.Quote},
} ;
static readonly Dictionary<ExpressionType, Func<Expression, Expression, BinaryExpression>> binExps = new Dictionary<ExpressionType, Func<Expression, Expression, BinaryExpression>>
{
{ ExpressionType.AndAlso, Expression.And},
{ ExpressionType.Modulo, Expression.Modulo},
{ ExpressionType.ExclusiveOr, Expression.ExclusiveOr},
{ ExpressionType.Power, Expression.Power},
{ ExpressionType.RightShift, Expression.RightShift},
{ ExpressionType.LeftShift, Expression.LeftShift},
{ ExpressionType.Multiply, Expression.Multiply},
{ ExpressionType.MultiplyChecked, Expression.MultiplyChecked},
{ ExpressionType.Divide, Expression.Divide},
{ ExpressionType.Add, Expression.Add},
{ ExpressionType.AddChecked, Expression.AddChecked},
{ ExpressionType.Subtract, Expression.Subtract},
{ ExpressionType.SubtractChecked, Expression.SubtractChecked},
{ ExpressionType.OrElse, Expression.OrElse},
{ ExpressionType.Or, Expression.Or},
{ ExpressionType.And, Expression.Add},
{ ExpressionType.GreaterThan, Expression.GreaterThan},
{ ExpressionType.GreaterThanOrEqual, Expression.GreaterThanOrEqual},
{ ExpressionType.LessThan, Expression.LessThan},
{ ExpressionType.LessThanOrEqual, Expression.LessThanOrEqual},
{ ExpressionType.NotEqual, Expression.NotEqual},
{ ExpressionType.Equal, Expression.Equal},
{ ExpressionType.ArrayIndex, Expression.ArrayIndex},
};
static readonly Dictionary<ExpressionType, string> m_formatStrings = new Dictionary<ExpressionType, string>
{
//Unary
{ ExpressionType.Not, "!{0}"},
{ ExpressionType.Negate, "-{0}"},
{ ExpressionType.NegateChecked, "-{0}"},
{ ExpressionType.UnaryPlus, "+{0}"},
//{ ExpressionType.Quote, "'{0}'"},
//Binary
{ ExpressionType.AndAlso, "{0} && {1}"},
{ ExpressionType.Modulo, "{0} % {1}"},
{ ExpressionType.ExclusiveOr, "{0} ^ {1}"},
{ ExpressionType.Power, "{0} toThePowerOf {1}"},
{ ExpressionType.RightShift, "{0} >> {1}"},
{ ExpressionType.LeftShift, "{0} << {1}"},
{ ExpressionType.Multiply, "{0} * {1}"},
{ ExpressionType.MultiplyChecked, "{0} * {1}"},
{ ExpressionType.Divide, "{0} / {1}"},
{ ExpressionType.Add, "{0} + {1}"},
{ ExpressionType.AddChecked, "{0} + {1}"},
{ ExpressionType.Subtract, "{0} - {1}"},
{ ExpressionType.SubtractChecked, "{0} - {1}"},
{ ExpressionType.OrElse, "{0} || {1}"},
{ ExpressionType.Or, "{0} | {1}"},
{ ExpressionType.And, "{0} & {1}"},
{ ExpressionType.GreaterThan, "{0} > {1}"},
{ ExpressionType.GreaterThanOrEqual, "{0} >= {1}"},
{ ExpressionType.LessThan, "{0} < {1}"},
{ ExpressionType.LessThanOrEqual, "{0} <= {1}"},
{ ExpressionType.NotEqual, "{0} != {1}"},
{ ExpressionType.Equal, "{0} == {1}"},
{ ExpressionType.ArrayIndex, "{0}[{1}]"},
};
private static void Expand(Expression body, IList<Expression> expantion)
{
if (body.NodeType != ExpressionType.AndAlso)
{
expantion.Add(body);
return;
}
var binExp = (BinaryExpression)body;
Expand(binExp.Left, expantion);
Expand(binExp.Right, expantion);
}
private static bool Eval<T>(ParameterExpression parameterExpression, Expression body, T subject, StringBuilder sb, out Exception lastCaught)
{
lastCaught = null;
var evaluator = new ExpressionEvaluator(subject, parameterExpression);
Step reval;
try
{
reval = evaluator.Visit(body);
sb.AppendLine(reval.Key);
return (bool) reval.Value;
}
catch (IndexOutOfRangeException e)
{
var result = new StringBuilder("BANG!\r\n" + e.Message);
lastCaught = e;
if (evaluator.Subject is ICollection)
{
result.AppendLine("(" + evaluator.Subject + ".Count == " + ((ICollection)evaluator.Subject).Count + ")");
}
evaluator.Result(result.ToString());
return false;
}
catch (TargetInvocationException e)
{
lastCaught = e.InnerException;
var result = new StringBuilder("BANG!\r\n" + e.InnerException.Message);
if (e.InnerException is ArgumentOutOfRangeException && subject is ICollection)
{
result.AppendLine("(" + evaluator.Subject + ".Count == " + ((ICollection)evaluator.Subject).Count + ")");
}
evaluator.Result(result.ToString());
return false;
}
catch (Exception e)
{
throw new AssertionException("While evaluating " + body + "\r\n" + e.Message, e);
}
finally
{
int i = 1;
foreach (string step in evaluator.evaluationSteps)
{
sb.AppendLine("\t" + i++ + ": " + step);
}
sb.AppendLine();
}
}
class Step
{
public Step(string key, object value)
{
Key = key;
Value = value;
}
public string Key { get; set; }
public object Value { get; set; }
}
class ExpressionEvaluator
{
private readonly object m_Parameter;
private readonly Expression m_ParamExp;
public readonly List<string> evaluationSteps = new List<string>();
public ExpressionEvaluator(object parameter, Expression paramExp)
{
m_Parameter = parameter;
m_ParamExp = paramExp;
}
public Step Visit(Expression exp)
{
if (exp is TypeBinaryExpression)
return Visit((TypeBinaryExpression) exp);
if (exp is UnaryExpression)
return Visit((UnaryExpression) exp);
if (exp is NewArrayExpression)
return Visit((NewArrayExpression)exp);
if (exp is ConstantExpression)
return Visit((ConstantExpression)exp);
if (exp is MethodCallExpression)
return Visit((MethodCallExpression) exp);
if (exp is MemberExpression)
return Visit((MemberExpression)exp);
if (exp is BinaryExpression)
return Visit((BinaryExpression) exp);
if (exp is ParameterExpression)
return Visit((ParameterExpression)exp);
return new Step(exp.ToString(), exp.ToString());
}
Step Visit(NewArrayExpression exp)
{
Array array = Array.CreateInstance(exp.Type, exp.Expressions.Count);
for (int i = 0; i < exp.Expressions.Count; i++)
{
array.SetValue(Visit(exp.Expressions[i]), i);
}
return new Step(exp.ToString(), array);
}
Step Visit(TypeBinaryExpression exp)
{
Step step = Visit(exp.Expression);
Subject = step.Value;
switch (exp.NodeType)
{
case ExpressionType.TypeIs:
SetNextStep(Display(Subject) + " is " + exp.TypeOperand);
return new Step(step.Key + " is " + exp.TypeOperand,
Result(exp.TypeOperand.IsAssignableFrom(Subject.GetType())));
}
throw new NotSupportedException(exp.NodeType.ToString());
}
Step Visit(UnaryExpression exp)
{
Step step = Visit(exp.Operand);
Subject = step.Value;
switch (exp.NodeType)
{
// case ExpressionType.Not:
// if (step.Value is bool)
// {
// return new Step("!" + step.Key, !(bool)step.Value);
// }
// return new Step("~" + step.Key, ~(long)step.Value);
// case ExpressionType.Negate:
// case ExpressionType.NegateChecked:
// return new Step("-" + step.Key, -(double)step.Value);
// case ExpressionType.UnaryPlus:
// return new Step("+" + step.Key, step.Value);
// case ExpressionType.Lambda:
// var lambdaExp = (LambdaExpression) exp.Operand;
// return new Step("compiledLamda" , lambdaExp.Compile());
case ExpressionType.Convert:
// try
// {
var type = exp.Type;
var value = step.Value;
object castedObject = Cast(value, type, exp.Method);
return new Step("((" + exp.Type.Name + ")" + step.Key + ")", castedObject);
// }
// catch (TargetInvocationException e)
// {
// if (e.InnerException is InvalidCastException)
// {
// try
// {
// return new Step("((" + exp.Type.Name + ")" + step.Key + ")",
// Convert.ChangeType(step.Value, exp.Type));
// }
// catch (Exception)
// {
// return new Step("((" + exp.Type.Name + ")" + step.Key + ")",
// exp.Method.Invoke(null, new [] { step.Value }));
// }
// }
// }
case ExpressionType.TypeAs:
SetNextStep(Display(Subject) + " as " + exp.Type);
return new Step(step.Key + " as " + exp.Type,
Result(exp.Type.IsAssignableFrom(Subject.GetType()) ? Cast(Subject, exp.Type, exp.Method) : null));
case ExpressionType.Quote:
return new Step("'" + exp.Operand + "'", exp.Operand);
default:
object val = Eval(exp.NodeType, exp.Type, step.Value);
var format = String.Format(m_formatStrings[exp.NodeType], Display(step.Value));
SetNextStep(format);
return new Step(String.Format(m_formatStrings[exp.NodeType], step.Key), Result(val));
}
}
private object Cast(object value, Type type, MethodInfo method)
{
try
{
MethodInfo castMethod = GetType().GetMethod("Cast").MakeGenericMethod(type);
return castMethod.Invoke(null, new[] {value});
}
catch (TargetInvocationException e)
{
if (e.InnerException is InvalidCastException /*e.InnerException.Message.Contains("cast is not valid")*/)
{
try
{
return Convert.ChangeType(value, type);
}
catch(InvalidCastException)
{
if (method != null)
return method.Invoke(null, new[] { value });
throw;
}
}
throw;
}
}
Step Visit(BinaryExpression binExp)
{
Step left = Visit(binExp.Left);
Step right = Visit(binExp.Right);
var lhs = left.Value;
var rhs = right.Value;
var subject = lhs;
MethodInfo method = binExp.Method;
Subject = subject;
if (method == null)
{
object value = Eval(binExp, lhs, rhs);
var format = String.Format(m_formatStrings[binExp.NodeType], Display(left.Value), Display(right.Value));
SetNextStep(format);
return new Step(String.Format(m_formatStrings[binExp.NodeType], left.Key, right.Key), Result(value));
}
if (method.IsStatic)
{
SetNextStep(method.Name + "(" + rhs + ")");
return new Step(method.Name + "(" + right.Key + ")", Result(method.Invoke(null, new [] { Subject, rhs})));
}
SetNextStep(Display(Subject) + "." + method.Name + "(" + rhs + ")");
return new Step(left.Key + "." + method.Name + "(" + right.Key + ")", Result(method.Invoke(Subject, new[] { rhs })));
}
public static object Eval(BinaryExpression binExp, object lhs, object rhs)
{
return Eval(binExp.NodeType, binExp.Type, lhs, rhs);
}
public static T Eval<T>(ExpressionType nodeType, object lhs, object rhs)
{
return (T) Eval(nodeType, typeof(T), lhs, rhs);
}
public static object Eval(ExpressionType nodeType, Type returnType, object lhs, object rhs)
{
var leftType = lhs == null?
rhs == null ?
typeof(object) :
rhs.GetType() :
lhs.GetType();
var rightType = rhs == null?
lhs == null?
typeof(object) :
lhs.GetType() :
rhs.GetType();
var genericMethod = typeof(ExpressionEvaluator).GetMethod("CallBin").MakeGenericMethod(leftType, rightType, returnType);
return genericMethod.Invoke(null, new[] { nodeType, lhs, rhs });
}
// ReSharper disable UnusedMemberInPrivateClass
public static object Eval(ExpressionType nodeType, Type returnType, object lhs)
{
var leftType = lhs == null? typeof(object) : lhs.GetType();
var genericMethod = typeof(ExpressionEvaluator).GetMethod("CallUni").MakeGenericMethod(leftType, returnType);
return genericMethod.Invoke(null, new[] { nodeType, lhs });
}
public static TRet CallBin<TLeft,TRight,TRet>(ExpressionType nodeType, TLeft lhs, TRight rhs)
{
var lambda = Expression.Lambda<Func<TRet>>(binExps[nodeType]
(Expression.Constant(lhs, typeof(TLeft)),
Expression.Constant(rhs, typeof(TRight))));
return lambda.Compile()();
}
public static TRet CallUni<TLeft,TRet>(ExpressionType nodeType, TLeft lhs)
{
Expression expression = Expression.Constant(lhs, typeof (TLeft));
var lambda = Expression.Lambda<Func<TRet>>(uniExps[nodeType](expression));
return lambda.Compile()();
}
// ReSharper restore UnusedMemberInPrivateClass
Step Visit(MethodCallExpression exp)
{
var step = exp.Object == null? new Step("null",null) : Visit(exp.Object);
Subject = step.Value;
if (Subject == null && !exp.Method.IsStatic)
{
SetNextStep("" + Display(Subject) + "." + exp.Method.Name + "(...)");
throw new TargetInvocationException(new Exception(Display(Subject) + " is null."));
}
var argValues = new List<object>();
var argSteps = new List<Step>();
foreach (var arg in exp.Arguments)
{
var stepArg = Visit(arg);
argValues.Add(stepArg.Value);
argSteps.Add(stepArg);
}
Subject = step.Value;
//Indexer
if (exp.Method.Name.StartsWith("get_"))
{
SetNextStep(Display(Subject) + "[" + ToDisplayString(argValues) + "]");
return new Step(step.Key + "[" + ToDisplayString(argSteps) + "]", Result(exp.Method.Invoke(Subject, argValues.ToArray())));
}
SetNextStep(Display(Subject) + "." + exp.Method.Name + "(" + ToDisplayString(argValues) + ")");
return new Step(step.Key + "." + exp.Method.Name + "(" + ToDisplayString(argSteps) + ")", Result(exp.Method.Invoke(Subject, argValues.ToArray())));
}
Step Visit(MemberExpression exp)
{
var step = Visit(exp.Expression);
Subject = step.Value;
SetNextStep("" + Display(Subject) + "." + exp.Member.Name);
if (Subject == null)
{
throw new NullReferenceException(step.Key + " is null. null." + exp.Member.Name + " goes bang.");
}
object result;
if (exp.Member is FieldInfo)
{
var field = (FieldInfo)exp.Member;
result = field.GetValue(Subject);
}
else
{
var prop = (PropertyInfo) exp.Member;
result = prop.GetGetMethod().Invoke(Subject, new object[] {});
}
return new Step(step.Key + "." + exp.Member.Name, Result(result));
}
public string Display(object o)
{
if (o == null) return "null";
if (o == m_Parameter) return m_ParamExp.ToString();
//Anonymous type
if (o.GetType().Name.StartsWith("<"))
{
return "_";
}
if (o is String)
{
return "\"" + o + '"';
}
if (o is char)
{
return "'" + o + "'";
}
//Is toString defined on this type?
if (!HasToStringDefined(o))
{
string name = o.GetType().Name;
return (Char.ToLower(name[0])) + name.Substring(1);
}
return o.ToString();
}
private static bool HasToStringDefined(object o)
{
return (o is string || o.ToString().Length < 20);
//return o.GetType().GetMethod("ToString", BindingFlags.DeclaredOnly) != null;
}
private Step Visit(ConstantExpression c)
{
return new Step(Display(c.Value),c.Value);
}
#pragma warning disable 168
Step Visit(ParameterExpression p)
#pragma warning restore 168
{
return new Step(Display(m_Parameter), m_Parameter);
}
private object m_tryStep;
public object Subject { get; set; }
private void SetNextStep(string from)
{
m_tryStep = from;
}
public object Result(object to)
{
evaluationSteps.Add(m_tryStep + " ==> " + Display(to));
return to;
}
private string ToDisplayString(IEnumerable arguments)
{
var sb = new StringBuilder();
foreach (object arg in arguments)
{
if (sb.Length != 0)
{
sb.Append(", ");
}
sb.Append(Display(arg));
}
return sb.ToString();
}
private static string ToDisplayString(IEnumerable<Step> arguments)
{
var sb = new StringBuilder();
foreach (Step arg in arguments)
{
if (sb.Length != 0)
{
sb.Append(", ");
}
sb.Append(arg.Key);
}
return sb.ToString();
}
// ReSharper disable UnusedMemberInPrivateClass
public static T Cast<T>(object o)
// ReSharper restore UnusedMemberInPrivateClass
{
return (T)o;
}
}
}
[TestFixture]
public class SpecExtentionsSpec
{
#pragma warning disable 184
// ReSharper disable RedundantToStringCall
[Test]
public void TestFailures()
{
const string str = "Fred";
str.AssertFalse(s=>(s as object) == null
&& s is int
&& s.ToString() == "Frd"
&& s[3] == 'e'
&& s.Length > 5
&& "Something really close and long"
== "Something reallly close and long"
);
}
class X : IComparable<X>
{
public string Name { get; set; }
public int CompareTo(X other)
{
return other.Name.CompareTo(Name);
}
}
[Test]
public void TestSuccess()
{
// bool t = true;
// "".Assert(s => (t && !t) || (t && !t));
var x = new X { Name = "Fred" };
var other = new X {Name = "Fred"};
x.Assert(z=>z.CompareTo(other) == 0);
/*
* could we do this?
* var query = from word in words
where word.Length > 4
select word.ToUpper();
* **/
const string str = "Fred";
str.Assert(
s => (s as object) == "Fred"
&& s is string
&& s.ToString() == "Fred"
&& !false
&& s.Length - 2 == 2
&& s[3] == 'd'
&& s.Length > 2
);
}
[Test]
public void BinOperatorTests()
{
int s = 1;
int j = 3;
int [] fib = { 1, 1, 2, 3, 5 };
1.Assert(n => n != j - s
&& fib[0] == 1 );
1.Assert(n => n + 1 == 2
&& n - 1 == 0
&& n % 2 == 1
&& (n ^ 1) == 0
&& n * 2 == 2
&& n / 2 == 0
&& ((double)n) / 2 == 0.5
&& n != 2
);
}
[Test]
public void UnaryOperatorTests()
{
1.Assert(n => -n == -1
&& !(n - 1 == 2)
&& n == +1
);
}
class A
{
public B Bee { get; set; }
}
class B
{
public string C;
}
class HasList
{
public IList<string> MyList { get { return new List<string> {"George", "Bob", "Ringo"}; } set { /*alas requres setter! */} }
}
class HasDict
{
public IDictionary<string, object> MyDict
{
get { return new Dictionary<string, object>
{
{"George", 1}, {"Bob", 2}, {"Ringo", 3}
}
;
} set { /*alas requres setter! */} }
}
class HasDictList
{
public IDictionary<string, B> MyDict
{
get
{
return new Dictionary<string, B>
{
{"George", new B{C = "1"}}, {"Bob", new B()}, {"Ringo", null}
}
;
}
set { /*alas requres setter! */}
}
}
[Test]
public void WhatsNotToLikeSuccess()
{
new HasDictList().Like(() => new HasDictList
{
MyDict = new Dictionary<string, B>
{
{"George", new B{C = "1"}},
{"Ringo", null }
}
});
//IDictionary
new HasDict().Like(() => new HasDict
{
MyDict = new Dictionary<string,object>
{
{"George", 1},
{"Ringo", 3}
}
});
//List
new HasList().Like(() => new HasList
{
MyList = new List<string>
{
"George",
"Ringo",
}
});
//Simple
new NetworkCredential("Fred", "Open sesame")
.Like(() => new NetworkCredential{ UserName = "Fred"} );
//Recursive
new A { Bee = new B { C = "Dr Dee" } }
.Like(() => new A { Bee = new B { C = "Dr Dee" } });
//TODO link a Like with custom asserts...
new NetworkCredential
{
UserName = "".Should(name=> name.Length > 4)
};
}
[Test]
public void WhatsNotToLikeFail()
{
new NetworkCredential("Fred", "Open sesame")
.NotLike(() => new NetworkCredential { UserName = "Freddy" });
new NetworkCredential
{
UserName = "".Should(name => name.Length > 4)
};
}
public class Fixture
{
public string m_field;
public string Property { get; set;}
}
[Test]
public void Throw_null_pointer_message_When_left_of_field_access_is_null()
{
try
{
var fixture = new Fixture();
Spec.That(() => fixture.m_field.Length == 0);
}
catch (AssertionException e)
{
Assert.That(e.Message.Contains("null"));
}
}
[Test]
public void Throw_null_pointer_message_When_left_of_property_access_is_null()
{
try
{
var fixture = new Fixture();
Spec.That(() => fixture.Property.Length == 0);
}
catch (AssertionException e)
{
Assert.That(e.Message.Contains("null"));
}
}
// ReSharper restore RedundantToStringCall
#pragma warning restore 184
[Test]
public void Should_be_able_to_assert_when_implicit_cast_is_called()
{
XElement xdoc = XElement.Parse(@"<xml><child/><child/></xml>");
Spec.That(() => xdoc.Descendants(/* implicit cast to XName */"child").Count() > 0);
}
}
static class YX
{
public static T Should<T>(this T subject, Expression<Func<T,bool>> func)
{
return default(T);
}
}
}
Friday, January 23, 2009
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment