简体   繁体   中英

Is it wrong to use Assert.That (rather than Assume.That) with a [Theory]?

interface IPoint
{
    int X { get; }
    int Y { get; }
}

static bool CoincidesWith(this IPoint self, IPoint other); // implementation unknown

I want to write a NUnit test that verifies my assumption about the meaning of CoincidesWith :

self.CoincidesWith(other) ⇔ ( self.X = other.X ) ∧ ( self.Y = other.Y )

The following is the most succinct test I've been able to come up with so far:

[Theory]
void CoincidesWith_Iff_CoordinatesAreEqual(IPoint self, IPoint other)
{
    bool coordinatesAreEqual = (self.X == other.X && self.Y == other.Y);
    Assert.That(self.CoincidesWith(other) == coordinatesAreEqual);
}

My questions, in descending order of importance, are:

  1. With [Theory] , is it considered wrong, or bad style, to use Assert.That instead of Assume.That ? ( The documentation seems to suggest that the latter should be used in conjunction with [Theory] . )
  2. Is this case indeed more suitable for a [Theory] rather than a [Test] ?

After some more thought, I've come to the conclusion that there is nothing wrong with my above solution.

Is this case indeed more suitable for a [Theory] rather than a [Test] ?

If the implementation for the CoincidesWith method were available for inspection (eg as source code), or at least well-documented, then there would be no need to make assumptions — I could simply look up what I need to know. In that case, a [Test] — or, as xUnit.net calls tests, a [Fact] — would seem more appropriate.

But since I have no access to the implementation for CoincidesWith , and the documentation is insufficient, I do need to make some assumption, or [Theory] , about the general working of the method.

With [Theory] , is it considered wrong, or bad style, to use Assert.That instead of Assume.That ?

No. It's just another tool to be used, and neither less nor more appropriate than Assert.That .

In the context of a [Theory] , Assume.That would seem to be the right means of putting additional constraints on the supplied [Datapoints] , while verifying the actual assumption (using those datapoints that make it past Assume.That ) is left to Assert.That .

An example can illustrate this. Let's try to write a test for this assumption:

Given an even integer a and an odd integer b , their product a * b is even.

Testing if a * b is even only makes sense once the preconditions are met. If a is not an even integer, or b is not an odd integer, the test should neither succeed nor fail; it should be inconclusive. And that's exactly what Assume.That helps achieve. The actual test, however, is left to Assert.That :

[Theory]
void GivenAnEvenIntegerAndAnOddInteger_ProductIsAnEvenInteger(int a, int b)
{
    Assume.That(a.IsEven());
    Assume.That(b.IsOdd());
    // note: the type system already ensures that `a` and `b` are integers.
    int product = a * b;
    Assert.That(product.IsEven());
    // note: the theory doesn't require `product` to be an integer, so even
    // if the type system didn't already assert this, we would not test for it.
}

[Datapoints]
int[] integers = { 1, 2, 3, 4, 5, 6, 7 };

static bool IsEven(this int integer) { … }
static bool IsOdd(this int integer) { … }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM