【问题标题】:How to modify the locale decimal separator within a single unit test in .NET?如何在 .NET 中的单个单元测试中修改语言环境小数点分隔符?
【发布时间】:2016-02-18 23:41:28
【问题描述】:

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

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

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

附加信息:

我正在 VB 中创建一个 .NET 库,并且我有一个类 MyClass 和一个 Encode(...) 函数,其中包括将数字信息作为文本写入。

该组件将用于不同计算机可能对“小数分隔符”(逗号或点)有不同配置的环境。然而,我的组件应该对此不敏感,这意味着它应该始终输出“点”(例如,在格式化数字时使用 System.Globalization.CultureInfo.InvariantCulture)。

我想编写一个单元测试,以确保即使系统区域设置小数分隔符设置为“逗号”而不是“点”,编码功能也能继续工作。我做了一些研究并想出了这样的东西:

公共子编码_CultureWithCommaSeparator_OutputMatchesTemplate() ... Dim oldCulture = Threading.Thread.CurrentThread.CurrentCulture ' 使用 "," 作为小数分隔符的区域性 Threading.Thread.CurrentThread.CurrentCulture = 新的 Globalization.CultureInfo("es-ES") 比较编码到模板(...) Threading.Thread.CurrentThread.CurrentCulture = oldCulture 结束子

CompareEncodedToTemplate 函数将使用MyClass.Encode 方法将信息写入MemoryStream,然后每行将其与模板文本文件 行进行比较,测试将失败当它们不相等时。

我想要“模拟”的是当语言环境的小数点分隔符不同于“点”时Encode 函数将如何运行。显然我的测试功能没有像我预期的那样工作:

我在我设置的小数点分隔符指向的计算机上运行测试,并且测试成功,所以我认为“我的编码功能将按我的意愿工作,因为测试通过了”。

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

提前谢谢你。

标签: c# .net vb.net unit-testing locale


【解决方案1】:

这是一个具有您所描述的方法的类:

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);
    }
}

这两个测试都成功执行。

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

【讨论】:

  • 你的“技巧”很有帮助,因为它让我重新思考我是如何编写测试的。谢谢。
【解决方案2】:

我的开发机器与我们的 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()));

【讨论】:

  • 编辑:调整结构以适应您的场景,您想在其中检查任何 ToString() 或其他内容中的显式文化。
【解决方案3】:

你应该考虑像这样使用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

【讨论】:

  • 谢谢,但我不是在问如何使用语言环境来格式化文本。我可以创建一个 NumberFormatInfo,但我不会将它传递给 Encode 函数(这是编写文本的函数)。我正在尝试创建一个测试,以确保编码功能将独立于系统如何配置区域设置。因此,我需要在单元测试中模拟“语言环境小数点的变化”。
  • 这仍然是正确的答案。真正的问题是所描述的 Encode() 函数不是为单元测试而设计的。
  • 对不起,我不同意。建议的解决方案是,首先使编码函数输出“,”而不是小数点,这不是预期的行为。或者以某种方式建议将“NumberFormatInfo”传递给编码函数。 Glenn Ferrie 的另一个答案实际上是在解决真正的问题。和乔纳森·狄金森一样。
【解决方案4】:

据我所知,使用"{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”,英文)-> 1234.57
  • 1234.567(“F”,de-DE)-> 1234,57
  • 1234(“F1”,en-US)-> 1234.0
  • 1234(“F1”,de-DE)-> 1234,0

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

【讨论】:

  • 我可以确保 String.Format("{0:0.0}", value) 在使用瑞典语作为语言环境的系统中以十进制逗号呈现。
  • 这和 String.Format("{0.0}", value) 不一样,不是吗?
  • 对不起,无论一个人多么认真地写一篇文章……总是会出错。我将在主帖中修复。谢谢。
猜你喜欢
  • 2013-12-27
  • 2015-03-24
  • 2016-03-25
  • 2016-01-14
  • 1970-01-01
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多