3
\$\begingroup\$

Wouldn't it be nice to just chain assertions after a method call or is it just me? I was thinking that it'd improve readability.

Instead of:

var myObject = _objectService.GetRandomObject();
Trace.Assert(myObject!=null);

it'll just be this:

_objectService.GetRandomObject().NeverNull();

with the extension method defined as

namespace System
{
    public static class AssertionExtensions
    {
        public static T NeverNull<T>(this T obj) 
            where T : class
        {
            Trace.Assert(obj != null);
            return obj;
        }
    }
}

We could make extensions for some of the most common assertions:

public static T Never<T>(this T obj, bool condition) 
    where T : class
{
    Trace.Assert(!condition);
    return obj;
}

public static T Always<T>(this T obj, bool condition) 
    where T : class
{
    Trace.Assert(condition);
    return obj;
}

If one prefers exceptions over asserts, that's a possibility too.

\$\endgroup\$
3
  • \$\begingroup\$ One may overloads != and == operator doing some side effects while comparing, so be careful whe utilizing this operators directly \$\endgroup\$ Oct 17, 2011 at 5:40
  • \$\begingroup\$ By providing a meningful name you have to explan what your code is doing insde the function call. this is a good code practice. For the method .NeverNull(), it is uncleat what is going on, is it a constraints, or what? functions call must include action verbs, see the Pattern & Practices books from the GO4. According to these books, you code is bad, because despite great things it is doing beneath the braces, you have to think about the proper utilization of these code again, and again... keeping in memory what these methods like .NeverDoItAgain(),.NeverDoIt(),.NeverDo(),.No() means... \$\endgroup\$ Oct 17, 2011 at 5:54
  • \$\begingroup\$ I'm not sure I understand the problem with the operators but I do get your point on naming. I considered these names since Asserts imply that a condition should "never" be true or that a condition is "always" false. Also, the words "Never" and "Always" are very strong words that I (so far) have rarely encountered in my day to day programming. \$\endgroup\$
    – Jonn
    Oct 17, 2011 at 7:47

3 Answers 3

3
\$\begingroup\$

I use Code Contracts to take care of my assertions.

Contract.Requires(customer.Age > 0);
Contract.Requires(!string.IsNullOrWhitespace(customer.Name));
Contract.Requires(customer.RegisteredProduct.Any());
Contract.Ensures(Contract.Result<SupportTicket> != null);

It can do several things that extension methods can never do:

  • Show you the source code of the tested expression (although FluentAssertions can approximate that by using expressions, it has the disadvantage of performance and being restricted to using their custom comparison methods (or the more verbose lambdas) as opposed to simple boolean evaluated expressions (like in Debug.Assert(...))
  • Perform assertions on return values, regardless of how many return statements you have in your method.
  • Expose additional metadata about the method via tools (so that IntelliSense shows the contracts).
  • Apply a contract to an interface, where all implementers automatically inherit the assertions.
  • Perform compile-time verifications of the contract, if you're using the Premium or Ultimate versions of Visual Studio. EDIT: Version 1.5 now also support the Professional edition.
\$\endgroup\$
2
\$\begingroup\$

Sounds like you guys are trying to re-invent the excellent FluentAssertions library..

\$\endgroup\$
5
  • \$\begingroup\$ Wow. This lib has everything! Can it do asserts on a regular (not a unit test) project? So far I've read that its mostly used in conjunction with TDD frameworks \$\endgroup\$
    – Jonn
    Oct 17, 2011 at 9:05
  • \$\begingroup\$ It is designed for use in unit testing but there's no reason you couldn't use it to assert your preconditions, if you prefer that kind of syntax over something like CodeContracts :) \$\endgroup\$
    – MattDavey
    Oct 17, 2011 at 9:20
  • \$\begingroup\$ If someone is able to write piece of code to cover 100% of the generic use cases (chaining syntax, code contracts style), why not just using it? \$\endgroup\$ Oct 17, 2011 at 14:25
  • \$\begingroup\$ Looks like I just might. I didn't know of those before this. Thanks. \$\endgroup\$
    – Jonn
    Oct 18, 2011 at 0:32
  • \$\begingroup\$ Another one worth checking out is Shouldly - shouldly.github.com \$\endgroup\$
    – MattDavey
    Oct 18, 2011 at 6:29
1
\$\begingroup\$

You may found it useful to support both coding techniques, by supplying both versions of a method call, generic and non-generic. One will be using an extension method parameters, mirrored and backed up with method using anonymous function references to as a parameters to static methods.

As an advantge of using this scenario you will get the most from generics and flexiblility of usage for your assertions library of static functions, thus allowed to be used with structures and other value types also. Additionally, i removed the class constraints, because there are already mirrored functions for use by value types in AssertionContracts static class.

I suppose it is better to use Func<T>, Func<T,U> function references where appropriate, just like in this sample (utilizing both extension functions and anonymous functions):

    public void ProcessQuery(Query obj, QueryParameter parameter)
    {
        obj
            .AssertNotNull()
            .AssertIsNotNullOrWhitespace((o) => o.SQLText)
            .AssertNull((o) => o.QueryResults)
            .AssertNotNull((o) => o.QueryParameters)
            .AssertPositive((o) => o.QueryParameters.Length)
            .AssertEquals((o) => o.QueryParameters[0].Name, parameter.Name);

        AssertionContracts.AssertTrue(() => obj != null);
        AssertionContracts.AssertIsNotNullOrWhitespace(() => obj.SQLText);
        AssertionContracts.AssertNull(() => obj.QueryResults);
        AssertionContracts.AssertNotNull(() => obj.QueryParameters);
        AssertionContracts.AssertPositive(() => obj.QueryParameters.Length);
        AssertionContracts.AssertEquals(() => obj.QueryParameters[0].Name, parameter.Name);
    }

or this:

public class SmtpServerAddress 
{
    public string UNC { get; set; }
    public long UID { get; set; } 
}
public class SmtpClient
{
    public SmtpServerAddress Server { get; private set; }

    public SmtpClient(SmtpServerAddress smtpServer)
    {
        AssertionContracts.ThrowOnTrue<ArgumentNullException>(() => smtpServer == null);
        Server = smtpServer;
    }
    public SmtpClient(SmtpServerAddress smtpServer, bool dummy1, bool dummy2)
    {
        smtpServer
            .RequireNotNull()
            .RequireIsNullOrWhitespace((obj) => obj.UNC)
            .RequireEqualsZero((obj) => obj.UID);
        Server = smtpServer;
    }  
}

Source code:

public static class AssertionContracts
{
    private static class Exceptions<U> where U : Exception, new()
    {
        public static T ThrowOnTrue<T>(T obj, Func<T, bool> function, params object[] args)
        {
            if (function(obj) == true)
            {
                Throw(obj, args);
            }
            return obj;
        }
        public static T ThrowOnFalse<T>(T obj, Func<T, bool> function, params object[] args)
        {
            if (function(obj) == false)
            {
                Throw(obj, args);
            }
            return obj;
        }
        public static void Throw<T>(T obj, params object[] args)
        {
            throw CreateException(obj, args);
        }
        private static U CreateException<T>(T obj, params object[] args)
        {
            return (U)Activator.CreateInstance(typeof(U), args);
        }
    }
    public class ContractException : Exception
    {
        public ContractException() : base() { }
        public ContractException(string message) : base(message) { }
        protected ContractException(SerializationInfo info, StreamingContext context) : base(info, context) { }
        public ContractException(string message, Exception innerException) : base(message, innerException) { }
    }
    public static T ThrowOnTrue<T, U>(this T obj, Func<T, bool> function, params object[] args) where U : Exception, new()
    {
        return AssertionContracts.Exceptions<ContractException>.ThrowOnTrue(obj, function);
    }
    public static T ThrowOnFalse<T, U>(this T obj, Func<T, bool> function, params object[] args) where U : Exception, new()
    {
        return AssertionContracts.Exceptions<ContractException>.ThrowOnFalse(obj, function);
    }
    public static void Throw<T, U>(this T obj, Func<T, bool> function, U ex) where U : Exception
    {
        AssertionContracts.Exceptions<ContractException>.Throw(obj, function);
    }
    public static T NoThrowContractException<T>(this T obj, Func<T, bool> function)
    {
        return AssertionContracts.Exceptions<ContractException>.ThrowOnTrue(obj, function);
    }
    public static T ThrowContractException<T>(this T obj, Func<T, bool> function)
    {
        return AssertionContracts.Exceptions<ContractException>.ThrowOnFalse(obj, function);
    }
    public static T AssertTrue<T>(this T obj, Func<T, bool> function)
    {
        Trace.Assert(function(obj) == true);
        return obj;
    }
    public static T AssertFalse<T>(this T obj, Func<T, bool> function)
    {
        Trace.Assert(function(obj) == false);
        return obj;
    }
    public static T AssertIsNullOrEmpty<T>(this T obj, Func<T, string> function)
    {
        Trace.Assert(string.IsNullOrEmpty(function(obj)));
        return obj;
    }
    public static T AssertIsNullOrWhitespace<T>(this T obj, Func<T, string> function)
    {
        Trace.Assert(string.IsNullOrWhiteSpace(function(obj)));
        return obj;
    }
    public static T AssertIsNotNullOrEmpty<T>(this T obj, Func<T, string> function)
    {
        Trace.Assert(!string.IsNullOrEmpty(function(obj)));
        return obj;
    }
    public static T AssertIsNotNullOrWhitespace<T>(this T obj, Func<T, string> function)
    {
        Trace.Assert(!string.IsNullOrWhiteSpace(function(obj)));
        return obj;
    }
    public static T AssertEquals<T, U>(this T obj, Func<T, U> function, U value)
    {
        Trace.Assert(object.Equals(function(obj), value));
        return obj;
    }
    public static T AssertNotEquals<T, U>(this T obj, Func<T, U> function, U value)
    {
        Trace.Assert(!object.Equals(function(obj), value));
        return obj;
    }
    public static T AssertDefault<T, U>(this T obj, Func<T, U> function)
    {
        Trace.Assert(object.Equals(function(obj), default(U)));
        return obj;
    }
    public static T AssertNonDefault<T, U>(this T obj, Func<T, U> function)
    {
        Trace.Assert(!object.Equals(function(obj), default(U)));
        return obj;
    }
    public static T AssertNotNull<T, U>(this T obj, Func<T, U> function)
    {
        Trace.Assert(!object.Equals(function(obj), default(U)));
        return obj;
    }
    public static T AssertNotNull<T>(this T obj)
    {
        Trace.Assert(!object.Equals(obj, default(T)));
        return obj;
    }
    public static T AssertNull<T, U>(this T obj, Func<T, U> function)
    {
        Trace.Assert(object.Equals(function(obj), default(U)));
        return obj;
    }
    public static T AssertNull<T>(this T obj)
    {
        Trace.Assert(object.Equals(obj, default(T)));
        return obj;
    }
    public static T AssertPositive<T>(this T obj, Func<T, int> function)
    {
        Trace.Assert(function(obj) > 0);
        return obj;
    }
    public static T AssertPositive<T>(this T obj, Func<T, long> function)
    {
        Trace.Assert(function(obj) > 0);
        return obj;
    }
    public static T AssertNegative<T>(this T obj, Func<T, int> function)
    {
        Trace.Assert(function(obj) < 0);
        return obj;
    }
    public static T AssertNegative<T>(this T obj, Func<T, long> function)
    {
        Trace.Assert(function(obj) < 0);
        return obj;
    }
    public static T AssertEqualsZero<T>(this T obj, Func<T, int> function)
    {
        Trace.Assert(function(obj) == 0);
        return obj;
    }
    public static T AssertEqualsZero<T>(this T obj, Func<T, long> function)
    {
        Trace.Assert(function(obj) == 0);
        return obj;
    }
    public static T AssertNotEqualsZero<T>(this T obj, Func<T, int> function)
    {
        Trace.Assert(function(obj) != 0);
        return obj;
    }
    public static T AssertNotEqualsZero<T>(this T obj, Func<T, long> function)
    {
        Trace.Assert(function(obj) != 0);
        return obj;
    }
    public static T AssertGreaterThan<T>(this T obj, Func<T, int> function, int value)
    {
        Trace.Assert(function(obj) > value);
        return obj;
    }
    public static T AssertGreaterThan<T>(this T obj, Func<T, long> function, long value)
    {
        Trace.Assert(function(obj) > value);
        return obj;
    }
    public static T AssertLessThan<T>(this T obj, Func<T, int> function, int value)
    {
        Trace.Assert(function(obj) < value);
        return obj;
    }
    public static T AssertLessThan<T>(this T obj, Func<T, long> function, long value)
    {
        Trace.Assert(function(obj) < value);
        return obj;
    }
    public static T AssertGreaterOrEqualsThan<T>(this T obj, Func<T, int> function, int value)
    {
        Trace.Assert(function(obj) >= value);
        return obj;
    }
    public static T AssertGreaterOrEqualsThan<T>(this T obj, Func<T, long> function, long value)
    {
        Trace.Assert(function(obj) >= value);
        return obj;
    }
    public static T AssertLessOrEqualsThan<T>(this T obj, Func<T, int> function, int value)
    {
        Trace.Assert(function(obj) <= value);
        return obj;
    }
    public static T AssertLessOrEqualsThan<T>(this T obj, Func<T, long> function, long value)
    {
        Trace.Assert(function(obj) <= value);
        return obj;
    }
    public static T AssertGreaterOrEqualsZero<T>(this T obj, Func<T, int> function)
    {
        Trace.Assert(function(obj) >= 0);
        return obj;
    }
    public static T AssertGreaterOrEqualsZero<T>(this T obj, Func<T, long> function, long value)
    {
        Trace.Assert(function(obj) >= 0);
        return obj;
    }
    public static T AssertLessOrEqualsZero<T>(this T obj, Func<T, int> function)
    {
        Trace.Assert(function(obj) <= 0);
        return obj;
    }
    public static T AssertLessOrEqualsZero<T>(this T obj, Func<T, long> function)
    {
        Trace.Assert(function(obj) <= 0);
        return obj;
    }
    public static T RequireTrue<T>(this T obj, Func<T, bool> function)
    {
        return obj.ThrowContractException((o) => function(obj) == true);
    }
    public static T RequireFalse<T>(this T obj, Func<T, bool> function)
    {
        return obj.ThrowContractException((o) => function(obj) == false);
    }
    public static T RequireIsNullOrEmpty<T>(this T obj, Func<T, string> function)
    {
        return obj.ThrowContractException((o) => string.IsNullOrEmpty(function(obj)));
    }
    public static T RequireIsNullOrWhitespace<T>(this T obj, Func<T, string> function)
    {
        return obj.ThrowContractException((o) => string.IsNullOrWhiteSpace(function(obj)));
    }
    public static T RequireIsNotNullOrEmpty<T>(this T obj, Func<T, string> function)
    {
        return obj.ThrowContractException((o) => !string.IsNullOrEmpty(function(obj)));
    }
    public static T RequireIsNotNullOrWhitespace<T>(this T obj, Func<T, string> function)
    {
        return obj.ThrowContractException((o) => !string.IsNullOrWhiteSpace(function(obj)));
    }
    public static T RequireEquals<T, U>(this T obj, Func<T, U> function, U value)
    {
        return obj.ThrowContractException((o) => object.Equals(function(obj), value));
    }
    public static T RequireEquals<T, U>(this T obj, T value)
    {
        return obj.ThrowContractException((o) => object.Equals(obj, value));
    }
    public static T RequireNotEquals<T, U>(this T obj, Func<T, U> function, U value)
    {
        return obj.ThrowContractException((o) => !object.Equals(function(obj), value));
    }
    public static T RequireNotEquals<T, U>(this T obj, T value)
    {
        return obj.ThrowContractException((o) => !object.Equals(obj, value));
    }
    public static T RequireDefault<T, U>(this T obj, Func<T, U> function)
    {
        return obj.ThrowContractException((o) => object.Equals(function(obj), default(U)));
    }
    public static T RequireDefault<T>(this T obj)
    {
        return obj.ThrowContractException((o) => object.Equals(obj, default(T)));
    }
    public static T RequireNonDefault<T, U>(this T obj, Func<T, U> function)
    {
        return obj.ThrowContractException((o) => !object.Equals(function(obj), default(U)));
    }
    public static T RequireNonDefault<T, U>(this T obj)
    {
        return obj.ThrowContractException((o) => !object.Equals(obj, default(T)));
    }
    public static T RequireNotNull<T, U>(this T obj, Func<T, U> function)
    {
        return obj.ThrowContractException((o) => !object.Equals(function(obj), default(U)));
    }
    public static T RequireNotNull<T>(this T obj)
    {
        return obj.ThrowContractException((o) => !object.Equals(obj, default(T)));
    }
    public static T RequireNull<T, U>(this T obj, Func<T, U> function)
    {
        return obj.ThrowContractException((o) => object.Equals(function(obj), default(U)));
    }
    public static T RequireNull<T>(this T obj)
    {
        return obj.ThrowContractException((o) => object.Equals(obj, default(T)));
    }
    public static T RequirePositive<T>(this T obj, Func<T, int> function)
    {
        return obj.ThrowContractException((o) => function(obj) > 0);
    }
    public static T RequirePositive<T>(this T obj, Func<T, long> function)
    {
        return obj.ThrowContractException((o) => function(obj) > 0);
    }
    public static T RequireNegative<T>(this T obj, Func<T, int> function)
    {
        return obj.ThrowContractException((o) => function(obj) < 0);
    }
    public static T RequireNegative<T>(this T obj, Func<T, long> function)
    {
        return obj.ThrowContractException((o) => function(obj) < 0);
    }
    public static T RequireEqualsZero<T>(this T obj, Func<T, int> function)
    {
        return obj.ThrowContractException((o) => function(obj) == 0);
    }
    public static T RequireEqualsZero<T>(this T obj, Func<T, long> function)
    {
        return obj.ThrowContractException((o) => function(obj) == 0);
    }
    public static T RequireNotEqualsZero<T>(this T obj, Func<T, int> function)
    {
        return obj.ThrowContractException((o) => function(obj) != 0);
    }
    public static T RequireNotEqualsZero<T>(this T obj, Func<T, long> function)
    {
        return obj.ThrowContractException((o) => function(obj) != 0);
    }
    public static T RequireGreaterThan<T>(this T obj, Func<T, int> function, int value)
    {
        return obj.ThrowContractException((o) => function(obj) > value);
    }
    public static T RequireGreaterThan<T>(this T obj, Func<T, long> function, long value)
    {
        return obj.ThrowContractException((o) => function(obj) > value);
    }
    public static T RequireLessThan<T>(this T obj, Func<T, int> function, int value)
    {
        return obj.ThrowContractException((o) => function(obj) < value);
    }
    public static T RequireLessThan<T>(this T obj, Func<T, long> function, long value)
    {
        return obj.ThrowContractException((o) => function(obj) < value);
    }
    public static T RequireGreaterOrEqualsThan<T>(this T obj, Func<T, int> function, int value)
    {
        return obj.ThrowContractException((o) => function(obj) >= value);
    }
    public static T RequireGreaterOrEqualsThan<T>(this T obj, Func<T, long> function, long value)
    {
        return obj.ThrowContractException((o) => function(obj) >= value);
    }
    public static T RequireLessOrEqualsThan<T>(this T obj, Func<T, int> function, int value)
    {
        return obj.ThrowContractException((o) => function(obj) <= value);
    }
    public static T RequireLessOrEqualsThan<T>(this T obj, Func<T, long> function, long value)
    {
        return obj.ThrowContractException((o) => function(obj) <= value);
    }
    public static T RequireGreaterOrEqualsZero<T>(this T obj, Func<T, int> function)
    {
        return obj.ThrowContractException((o) => function(obj) >= 0);
    }
    public static T RequireGreaterOrEqualsZero<T>(this T obj, Func<T, long> function, long value)
    {
        return obj.ThrowContractException((o) => function(obj) >= 0);
    }
    public static T RequireLessOrEqualsZero<T>(this T obj, Func<T, int> function)
    {
        return obj.ThrowContractException((o) => function(obj) <= 0);
    }
    public static T RequireLessOrEqualsZero<T>(this T obj, Func<T, long> function)
    {
        return obj.ThrowContractException((o) => function(obj) <= 0);
    }
    public static void ThrowOnFalse<U>(Func<bool> function, params object[] args) where U : Exception, new()
    {
        if (function() == false)
        {
            throw (U)Activator.CreateInstance(typeof(U), args);
        }
    }
    public static void ThrowOnTrue<U>(Func<bool> function, params object[] args) where U : Exception, new()
    {
        if (function() == true)
        {
            throw (U)Activator.CreateInstance(typeof(U), args);
        }
    }
    public static void Throw<U>(Func<bool> function, U ex) where U : Exception
    {
        if (function() == false)
        {
            throw ex;
        }
    }
    public static void AssertTrue(Func<bool> function)
    {
        Trace.Assert(function() == true);
    }
    public static void AssertFalse(Func<bool> function)
    {
        Trace.Assert(function() == false);
    }
    public static void AssertIsNullOrEmpty(Func<string> function)
    {
        Trace.Assert(string.IsNullOrEmpty(function()));
    }
    public static void AssertIsNullOrWhitespace(Func<string> function)
    {
        Trace.Assert(string.IsNullOrWhiteSpace(function()));
    }
    public static void AssertIsNotNullOrEmpty(Func<string> function)
    {
        Trace.Assert(!string.IsNullOrEmpty(function()));
    }
    public static void AssertIsNotNullOrWhitespace(Func<string> function)
    {
        Trace.Assert(!string.IsNullOrWhiteSpace(function()));
    }
    public static void AssertEquals<U>(Func<U> function, U value)
    {
        Trace.Assert(object.Equals(function(), value));
    }
    public static void AssertNotEquals<U>(Func<U> function, U value)
    {
        Trace.Assert(!object.Equals(function(), value));
    }
    public static void AssertDefault<U>(Func<U> function)
    {
        Trace.Assert(object.Equals(function(), default(U)));
    }
    public static void AssertNonDefault<U>(Func<U> function)
    {
        Trace.Assert(!object.Equals(function(), default(U)));
    }
    public static void AssertNotNull<U>(Func<U> function)
    {
        Trace.Assert(!object.Equals(function(), default(U)));
    }
    public static void AssertNull<U>(Func<U> function)
    {
        Trace.Assert(object.Equals(function(), default(U)));
    }
    public static void AssertPositive(Func<int> function)
    {
        Trace.Assert(function() > 0);
    }
    public static void AssertPositive(Func<long> function)
    {
        Trace.Assert(function() > 0);
    }
    public static void AssertNegative(Func<int> function)
    {
        Trace.Assert(function() < 0);
    }
    public static void AssertNegative(Func<long> function)
    {
        Trace.Assert(function() < 0);
    }
    public static void AssertEqualsZero(Func<int> function)
    {
        Trace.Assert(function() == 0);
    }
    public static void AssertEqualsZero(Func<long> function)
    {
        Trace.Assert(function() == 0);
    }
    public static void AssertNotEqualsZero(Func<int> function)
    {
        Trace.Assert(function() != 0);
    }
    public static void AssertNotEqualsZero(Func<long> function)
    {
        Trace.Assert(function() != 0);
    }
    public static void AssertGreaterThan(Func<int> function, int value)
    {
        Trace.Assert(function() > value);
    }
    public static void AssertGreaterThan(Func<long> function, long value)
    {
        Trace.Assert(function() > value);
    }
    public static void AssertLessThan(Func<int> function, int value)
    {
        Trace.Assert(function() < value);
    }
    public static void AssertLessThan(Func<long> function, long value)
    {
        Trace.Assert(function() < value);
    }
    public static void AssertGreaterOrEqualsThan(Func<int> function, int value)
    {
        Trace.Assert(function() >= value);
    }
    public static void AssertGreaterOrEqualsThan(Func<long> function, long value)
    {
        Trace.Assert(function() >= value);
    }
    public static void AssertLessOrEqualsThan(Func<int> function, int value)
    {
        Trace.Assert(function() <= value);
    }
    public static void AssertLessOrEqualsThan(Func<long> function, long value)
    {
        Trace.Assert(function() <= value);
    }
    public static void AssertGreaterOrEqualsZero(Func<int> function)
    {
        Trace.Assert(function() >= 0);
    }
    public static void AssertGreaterOrEqualsZero(Func<long> function, long value)
    {
        Trace.Assert(function() >= 0);
    }
    public static void AssertLessOrEqualsZero<T>(Func<int> function)
    {
        Trace.Assert(function() <= 0);
    }
    public static void AssertLessOrEqualsZero(Func<long> function)
    {
        Trace.Assert(function() <= 0);
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Is it really necessary to support this? I'm aiming for more readable code (and less typing). Adding the Func delegates seem to make it needlessly complex to the point that one might prefer to just call the original Trace/Debug Assert function. \$\endgroup\$
    – Jonn
    Oct 17, 2011 at 5:02
  • \$\begingroup\$ @John: Is this not enougth elegant code for you? : obj .AssertTrue((o) => obj != null) .AssertIsNotNullOrWhitespace((o) => o.SQLText) .AssertNull((o) => o.QueryResults) .AssertNotNull((o) => o.QueryParameters) .AssertPositive((o) => o.QueryParameters.Length) .AssertEquals((o) => o.QueryParameters[0].Name, parameter.Name); \$\endgroup\$ Oct 17, 2011 at 5:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.