17

I am currently developing a C# P/invoke wrapper to a DLL that is part of my product. I have no experience with C# and this is the first significant C# coding I have done. I am acutely aware that I am lacking a lot of knowledge of the finer points and idioms of the language.

My question concerns the unit tests that I am writing for which I am using NUnit. I have a need to compare the values of double[] variables. If I use Assert.AreEqual(...) to do this then the values are compared for exact equality. However, I would like to compare up to a tolerance. There are AreEqual() overloads for scalar real values that admit a delta parameter. However, I have not been able to find an equivalent for arrays. Have I missed something obvious?

At the moment I have solved the problem with the following code:

class Assert : NUnit.Framework.Assert
{
    public static void AreEqual(double[] expected, double[] actual, double delta)
    {
        AreEqual(expected.Length, actual.Length);
        for (int i = 0; i < expected.Length; i++)
        {
            AreEqual(expected[i], actual[i], delta);
        }
    }
}

Whilst this appears to work I wonder if there is a cleaner solution available. In particular I am concerned that using the same name for my derived class is, not only poor style, but could lead to un-anticipated problems down the road.

I would have like to use extension methods but I understand they are only viable for when there is an instance of the class under extension. Of course, I only ever call static methods on the Assert class.

I'm sorry if this seems a bit nebulous, but my instincts tell me that I'm not doing this the best way and I would like to know how to do it right.

Petr Krampl
  • 816
  • 9
  • 11
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    Did you try [Extension Methods](http://msdn.microsoft.com/en-us/library/bb383977.aspx)? – Ofer Zelig Nov 17 '11 at 10:06
  • 6
    @Ofer Zelig while i gave you the +1 i remembered that Extension Methods, while defined static, only work on instances. – dowhilefor Nov 17 '11 at 10:22
  • So as it seems now, that can't be done (unless you're implementing wrapper class, lots of hard and ugly work). See [this](http://stackoverflow.com/questions/249222/can-i-add-extension-methods-to-an-existing-static-class/435617#435617) and [this](http://madprops.org/blog/static-extension-methods/). – Ofer Zelig Nov 17 '11 at 10:31

4 Answers4

10

Since the introduction of the fluent assertion syntax in NUnit, the Within() method has been available for this purpose:

double actualValue = 1.989;
double expectedValue = 1.9890;
Assert.That(actualValue, Is.EqualTo(expectedValue).Within(0.00001));
Assert.That(actualValue, Is.EqualTo(expectedValue).Within(1).Ulps);
Assert.That(actualValue, Is.EqualTo(expectedValue).Within(0.1).Percent);

For collections, the default behaviour of Is.EqualTo() is to compare the collections' members individually, with these individual comparisons being modified by Within(). Hence, you can compare two arrays of doubles like so:

var actualDoubles = new double[] {1.0 / 3.0, 0.7, 9.981};
var expectedDoubles = new double[] { 1.1 / 3.3, 0.7, 9.9810};
Assert.That(actualDoubles, Is.EqualTo(expectedDoubles).Within(0.00001));
Assert.That(actualDoubles, Is.EqualTo(expectedDoubles).Within(1).Ulps);
Assert.That(actualDoubles, Is.EqualTo(expectedDoubles).Within(0.1).Percent);

This will compare each element of actualDoubles to the corresponding element in expectedDoubles using the specified tolerance, and will fail if any are not sufficiently close.

Peter
  • 3,619
  • 3
  • 31
  • 37
  • 1
    Thanks a lot for this. It looks like it's the way I am expected to solve the problem. I appreciate your time here. Thank you so much. As a matter of personal taste, I find these fluent interfaces a bit opaque. Once you've written it down, it kind of makes sense, but you have to crack the code in order to write it the first time! – David Heffernan Oct 07 '13 at 10:08
  • 1
    I think the trick with fluent interfaces of this sort is 'dot-driven development'. If you're using an IDE like Visual Studio, you only need to know the first 'link in the chain', e.g. `Is`, then you can hit `.` and Intellisense will show you all the options for your 'next step'. I learned about many features like `Within()` that way, rather than by reading the NUnit docs. – Peter Oct 07 '13 at 10:30
  • Imo the 'trick' with fluent interfaces is that you have to keep in mind that it isn't supposed to make sense or be readable, it's primarily supposed to look cool. I can't think of any other explanation for the introduction of abominations like 'Assert.That(actual, Is.Not.Null.And.Not.Empty)'. I can't imagine anyone would have the audacity to claim that a 6 element deep object graph to perform a simple comparision is better by any objective measure than the Assert.IsNotNullOrEmpty which seemed to work perfectly well for 40 years without anyone complaining. – Neutrino Dec 12 '18 at 16:29
4

I had a need to create custom assert, in your case there was an alternative provided by the framework. However this didn't work when I wanted to have a completely custom assert. I solved this by adding a new static class calling into nunit.

public static class FooAssert
{
     public static void CountEquals(int expected, FooConsumer consumer)
     {
         int actualCount = 0;
         while (consumer.ConsumeItem() != null)
             actualCount++;

         NUnit.Framework.Assert.AreEqual(expected, actualCount);
     }
}

Then in a test

[Test]
public void BarTest()
{
    // Arrange
    ... 

    // Act
    ...

    // Assert
    FooAssert.CountEquals(1, fooConsumer);
}

I know I am a little bit late for the party, it might still be useful for somebody

oleksii
  • 35,458
  • 16
  • 93
  • 163
2

I think what I would have done is simply define a function somewhere in your test harness

public static bool AreEqual( double[], double[], double delta )

that does the comparison and returns true or false appropriately. In your Test you simply write :

Assert.IsTrue( AreEqual( expected, result, delta ) ) ;
Tom Carter
  • 2,938
  • 1
  • 27
  • 42
  • Thanks for this. It's certainly a good idea. The downside for me is that I found that when I tried to implement this I had to reimplement the `delta` comparison that comes for free when you call AreEqual with real scalar inputs. So the answer of @dowhilefor seemed to fit my needs better this time. Thanks for caring! – David Heffernan Nov 17 '11 at 13:09
2

"Better" is always a matter of taste. In this case, i would say yes. You should make your own assert, without subclassing the nunit assert. Nunit already has multiple Assert classes with different specific assertions. Like CollectionAssert. Otherwise your method is fine.

dowhilefor
  • 10,971
  • 3
  • 28
  • 45
  • Thanks for this. I hadn't spotted the other `XXXAssert` classes. So I added an `ArrayAssert` class and moved my code in there. It feels quite natural now and my bad vibes have receded. – David Heffernan Nov 17 '11 at 13:07