Updated post: In order to avoid confusion about what I am and am not doing, I have edited this post radically to include a complete example of the code that causes this problem. In order to make this post readable anyway, all the code is posted at the bottom.
Background:
I am writing a fluent interface for testing (I know it's been done, but half of the purpose is to learn how it works...), in which I want to verify that myNumber
is between 3 and 10 with a code line like
myNumber.ShouldBeLessThan(10).And.ShouldBeGreaterThan(10);
myListOfCars.ShouldNotBeNull().And.ShouldBeA<IEnumerable<Car>>();
I think you can see by reading the second line, what it is supposed to verify. There are more complicated test cases, of course...
To enable the .And
syntax, I have introduced a helper type called AndHelper
, which is returned by each verification method and which has a property And
that returns whatever was tested. So .And
in the previous example, should return myNumber
so I can test another condition as well.
I'm using Code Contracts, and among other things I'm verifying that the this
argument of some of these extensions is non-null. This is that causes my problem.
My problem:
When running the Code Contract checks on my code, I get a bunch of warnings that the non-null requirement on for example ShouldBeA<T>
cannot be verified. I've tried to solve this by subclassing AndHelper<T>
with two classes, ReferenceAndHelper<T>
and StructAndHelper<T>
, and ReferenceAndHelper<T>
has contracts that should guarantee that the non-null requirement is met. However, this does not seem to work.
For each time I use one of these test extensions, I get two warning messages. One stating that the contract "instance != null" could not be verified, and the other stating the location. The first points to the line where I use the method (for example line 2 in my first example) and the second one to the line where the contract is specified, marked with // (1)
in my code.
My code:
Please bear with me that this part of the post is quite lengthy. I don't know what SO guidelines are on posting large chunks of code (that is still relevant), but if there is a better way, please enlighten me.
Note that there is code in this section that does not cause this specific error, but that introduces limits on the solution. For example, I must have a type (AndHelper<T>
or a subclass) that is class/struct ignorant.
A couple of the tests:
// This test requires that instance != null, and therefore works
// with ReferenceAndHelper<T>
public static ReferenceAndHelper<T> ShouldBeA<T>(this object instance, string message = "")
where T : class
{
Contract.Requires<ArgumentNullException>(instance != null); // (1)
Contract.Ensures(Contract.Result<ReferenceAndHelper<T>>() != null);
Assert.IsInstanceOf<T>(instance, message.AsNullIfWhitespace() ?? string.Format("ShouldBeA<{0}> failed.", typeof(T).Name));
return new ReferenceAndHelper<T>((T)instance);
}
// This test should work for both class and struct types T, and therefore
// cannot decide between StructAndHelper<T> and ReferenceAndHelper<T>.
// The base class is used.
public static AndHelper<T> ShouldBeGreaterThan<T>(this T actual, T expected, string message = "")
where T : IComparable
{
Contract.Ensures(Contract.Result<AndHelper<T>>() != null);
(actual.CompareTo(expected) > 0).ShouldBeTrue(message.AsNullIfEmpty() ?? string.Format("ShouldBeGreaterThan failed. {0} is not greater than {1}", actual.ToString(), expected.ToString()));
return new AndHelper<T>(actual);
}
// This is the test that returns the AndHelper<T> that .And is called on.
// It is, as you can see, in all possible ways specified that this will be a
// ReferenceAndHelper<T>, which has contracts to ensure that the value is not null.
public static ReferenceAndHelper<T> ShouldNotBeNull<T>(this T value, string message = "")
where T : class
{
Contract.Requires<ArgumentNullException>(value != null);
Contract.Ensures(Contract.Result<ReferenceAndHelper<T>>() != null);
Assert.IsNotNull(value, message.AsNullIfWhitespace() ?? "ShouldNotBeNull failed.");
return new ReferenceAndHelper<T>(value);
}
The AndHelper<T>
class:
public class AndHelper<T>
{
protected readonly T val;
public AndHelper(T value)
{
this.val = value;
}
public virtual T And { get { return this.val; } }
}
The two subclasses, ReferenceAndHelper<T>
:
public class ReferenceAndHelper<T> : AndHelper<T>
where T : class
{
public ReferenceAndHelper(T value)
: base(value)
{
Contract.Requires(value != null);
}
public override T And
{
get
{
Contract.Ensures(Contract.Result<T>() != null);
return val;
}
}
[ContractInvariantMethod]
void ValueIsNotNullInvariant()
{
Contract.Invariant(this.val != null);
}
}
and StructAndHelper<T>
:
public class StructAndHelper<T> : AndHelper<T>
where T : struct
{
public StructAndHelper(T value)
: base(value)
{
}
public override T And
{
get
{
return this.val;
}
}
}