如何在 .NET 的单个单元测试中修改语言环境小数点分隔符?

How to modify the locale decimal separator within a single unit test in .NET?

在 .NET(VB 或 C#)中并使用 Microsoft.VisualStudio.TestTools.UnitTesting 进行单元测试:

如何在单元测试中在本地有效地更改区域设置小数点分隔符,以便 String.Format("{0:0.0}", aDouble)(AFAIK 依赖于区域设置)生成带有 修改后的字符串语言环境?

编辑: 注意:我不是在问如何使用特定语言环境输出文本。我在问如何在单元测试中更改区域设置的小数点分隔符,以模拟在具有不同小数点分隔符的系统中会发生什么。 我不是从单元测试代码中调用 String.Format,而是从测试功能中调用 String.Format。

附加信息:

我正在 VB 中创建一个 .NET 库,我有一个 class MyClass 带有一个 Encode(...) 函数,除其他外,它正在写入数字信息作为文本。

该组件将在不同计算机可能对 "decimal separator"(逗号或点)有不同配置的环境中使用。但是,我的组件应该对此不敏感,这意味着它应该始终输出 "point"(例如,在格式化数字时使用 System.Globalization.CultureInfo.InvariantCulture)。

我想编写一个单元测试以确保即使系统区域设置小数点分隔符设置为 "comma" 而不是 "point",编码功能也能继续工作。我做了一些研究,得出了这样的结论:

 Public Sub Encode_CultureWithCommaSeparator_OutputMatchesTemplate()
  ...
  Dim oldCulture = Threading.Thread.CurrentThread.CurrentCulture
  ' A culture that uses "," as decimal separator
  Threading.Thread.CurrentThread.CurrentCulture = New Globalization.CultureInfo("es-ES")
  CompareEncodedToTemplate(...)
  Threading.Thread.CurrentThread.CurrentCulture = oldCulture
End Sub

CompareEncodedToTemplate 函数将使用 MyClass.Encode 方法将信息写入 MemoryStream,然后将其与 模板文本文件一行一行,不相等时测试失败

我想要 "simulate" 的是当区域设置的小数点分隔符不同于 "point" 时 Encode 函数将如何运行。显然我的测试功能没有像我预期的那样工作:

我运行在我设置的小数点分隔符指向的计算机上测试,测试成功,所以我想"my encode function will work as I want, because the test passed"。

然而我运行在电脑上测试,小数分隔符设置为逗号,测试失败。我意识到这是因为在我的 Encode 逻辑中我在格式化双精度数时错过了使用 InvariantCulture。这意味着 我的测试没有按预期工作,因为我应该能够在第一台计算机上检测到这种情况(因为这是我想要创建测试的原因)。

提前致谢。

据我所知,使用 "{0.0}" 格式化字符串将始终呈现为小数。

String.Format("{0:0}", 1.3); // Prints 1.3 regardless of culture

您必须更一般地指定它:

String.Format("{0:f}", 1.3); // Prints 1,3 if de-DE for example

查看字符串的标准数字格式 here。然后根据单元测试范围上下文中的当前文化,它会相应地呈现字符串。

例如:

  • 1234.567 ("F", en-US) -> 1234.57
  • 1234.567 ("F", de-DE) -> 1234,57
  • 1234 ("F1", en-US) -> 1234.0
  • 1234 ("F1", de-DE) -> 1234,0

在上述 link 中有无数格式化数字的例子。希望对您有所帮助。

您应该考虑像这样使用 NumberFormatInfo

var nfi = new System.Globalization.NumberFormatInfo();
nfi.NumberDecimalSeparator = ",";
var formatted = (10.01).ToString("N", nfi);

注意:NumberFormatInfo 对货币和其他数字有单独的设置

来自 MSDN:https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo(v=vs.110).aspx

运行 正是我的开发机器与我们的 CI 服务器的这个问题。我写了以下结构来帮助:

public struct CultureContext : IDisposable
{
    public static readonly CultureInfo CommaCulture = new CultureInfo("en-us")
    {
        NumberFormat =
        {
            CurrencyDecimalSeparator = ",",
            NumberDecimalSeparator = ",",
            PercentDecimalSeparator = ","
        }
    };

    public static readonly CultureInfo PointCulture = new CultureInfo("en-us")
    {
        NumberFormat =
        {
            CurrencyDecimalSeparator = ".",
            NumberDecimalSeparator = ".",
            PercentDecimalSeparator = "."
        }
    };

    private readonly CultureInfo _originalCulture;

    public CultureContext(CultureInfo culture)
    {
        _originalCulture = Thread.CurrentThread.CurrentCulture;
        Thread.CurrentThread.CurrentCulture = culture;
    }

    public void Dispose()
    {
        Thread.CurrentThread.CurrentCulture = _originalCulture;
    }

    public static void UnderBoth(Action test)
    {
        using (new CultureContext(PointCulture))
        {
            test();
        }

        using (new CultureContext(CommaCulture))
        {
            test();
        }
    }
}

然后您可以这样测试它:

CultureContext.UnderBoth(() => Assert.AreEqual("1.1", sut.ToString()));

这里有一个 class,其方法与您描述的一样:

public class Class1
{
    public string FormatSpecial(double d) {
        return string.Format("{0:0.0}", d);
    }
}

这是我的单元测试:

[TestClass]
public class UnitTest1
{
    Sample.Class1 instance;

    [TestInitialize]
    public void InitTests()
    {
        instance = new Sample.Class1();
    }

    [TestMethod]
    public void TestMethod1()
    {
        var result = instance.FormatSpecial(5.25);
        Assert.AreEqual("5.3", result);
    }

    [TestMethod]
    public void TestMethod2()
    {
        Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("es-ES");
        var result = instance.FormatSpecial(5.25);
        Assert.AreEqual("5,3", result);
    }
}

这两个测试都成功执行。

技巧 是在 运行 您正在测试的操作之前为测试中的线程设置文化信息。