Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Actual and expected values in NUnit test for format decimal extension not the same

I wrote the following extension method:

public static class DecimalExtensions
{
   public static string FormatCurrency(this decimal instance)
   {
      return string.Format("{0:c}", instance);
   }
}

The NUnit test:

[TestFixture]
public class DecimalExtensionsTests
{
   [Test]
   public void Format_should_return_formatted_decimal_string()
   {
      // Arrange
      decimal amount = 1000;

      // Act
      string actual = amount.FormatCurrency();

      // Assert
      Assert.AreEqual("R 1 000,00", actual);
   }
}

My test is failing and I am not sure why. The error that I get is the following:

String lengths are both 10. Strings differ at index 3.
  Expected: "R 1 000,00"
  But was:  "R 1 000,00"
  --------------^
like image 879
Brendan Vogt Avatar asked Oct 10 '22 00:10

Brendan Vogt


1 Answers

Your problem indeed comes from different space representation in numeric format. Space you're having troubles with is defined in NumberFormatInfo class' CurrencyGroupSeparator property. If you check character codes of both standard ASCII space and currency group separator space with the following snippet

Console.WriteLine("Space code: {0}", (Int32)' ');
var separator = Thread.CurrentThread.CurrentCulture.NumberFormat
    .CurrencyGroupSeparator;
Console.WriteLine("Currency separator code: {0}", (Int32)separator[0]);

... you'll find that it prints 32 and 160 respectively. This is why your string comparison fails.

For the purpose of unit testing, you can set the separator to actual ASCII space, like this:

Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyGroupSeparator = " ";

However, I would advise against it. You need to consider what happens when developer with other culture settings will run your unit test. Most likely it will fail, as resulting strings might differ. To make your method more usable (and unit tests more isolated), simply add overload that accepts culture info:

public static string FormatCurrency(this decimal instance)
{
   return instance.FormatCurrency(Thread.CurrentThread.CultureInfo);
}

public static string FormatCurrency(this decimal instance, CultureInfo culture)
{
    return string.Format(culture, "{0:c}", instance);
}

In your unit test, you write test against second method, with some well-known and easy to verify culture settings (might even fix the space to make it easier):

[Test]
public void FormatCurrency_should_return_formatted_decimal_string()
{
    decimal amount = 1000;
    var culture = CultureInfo.CreateSpecificCulture("en-us");
    // replacing space (160) with space (32)
    culture.NumberFormat.CurrencyGroupSeparator = " ";

    // Act
    string actual = amount.FormatCurrency(culture);

    // Assert
    Assert.AreEqual("$1 000.00", actual);
}

And in your real application you simply use culture-less overload and let the culture be the one from user's current settings.

like image 82
k.m Avatar answered Oct 18 '22 00:10

k.m