将字符串与`string.Format`模板

时间:2017-06-13 18:02:43

标签: c# .net

假设我有一个类,其方法在某种条件下返回错误消息,如下所示:

public class MyClass{
     public const string CertainErrorTemplate = "Can't find order {0}";

     public string CheckForCertainError(){
         ...
         else
            return string.Format(CertainErrorTemplate, orderId);
     } 
}

如何才能最好地编写单元测试,以确保返回的错误消息与模板具有相同的“形式”?如果我在单元测试中硬编码字符串文本的一部分,那么测试会(在我看来,错误地)中断,如果我稍后更新了这个词:...

另外:同样的问题,除了表格的消息:

$"Can't find order {orderId}"

我想我可以创建并返回所有实现.ToString()(并测试message is MyErrorClass)的消息类,但这感觉......详细。建议?

3 个答案:

答案 0 :(得分:2)

  

如果我在单元测试中硬编码字符串文本的一部分,它会   如果我稍后更新了这个词,那就打破......

想想你的目标。如果目标是检查返回的错误是否与某个公共模板匹配,那么在单元测试断言中使用CertainErrorTemplate是有意义的。每当您更改CertainErrorTemplate时,单元测试都会检查返回的值是否也发生了变化。当错误消息格式与公共模板不匹配时,测试将失败。

但通常,目标是不同的 - 您想检查返回的消息是否与某种格式相匹配 - 即包含一些具有给定订单ID的文本在正确的位置。 CertainErrorTemplate只是一个实施细节,不应该打扰你。这就是为什么我不建议在代码和单元测试之间共享字符串。更重要的是,如果您偶尔或有意更改CertainErrorTemplate,您的测试应该会失败。例如。 FluentAssertions

sut.CheckForCertainError().Should().Be("Can't find order 42")

我还建议不要使用硬编码的订单ID - 每次生成它并验证错误消息是否具有所需的格式并包含给定的订单ID:

sut.CheckForCertainError().Should().Be($"Can't find order {generatedId}")

您的SUT是否使用CertainErrorTemplate并不重要 - 如果实际结果与预期字符串不匹配,则测试应该失败。我已经看到一些在测试中使用共享字符串的情况,并且在修改公共消息模板之后,没有一个测试失败,尽管实际消息有正字错误。

答案 1 :(得分:0)

我将错误创建文本从逻辑中删除了:

public interface IErrorMessage{
    void CantFindTemplate(int templateId);
}


public class MyClass{

     private IErrorMessage _errorMessage;

     ctor(IErrorMessage errorMessage){

        _errorMessage = errorMessage

     }

     public string CheckForCertainError(){
         ...
         else
            _exceptionHelper.CantFindTemplate(templateId);
     } 
}

然后您可以在测试中验证它。

答案 2 :(得分:0)

您可以使用一个小帮助方法将模板拆分到其各个部分,并检查所有模板部分是否都是消息的一部分:

public static void AssertMatchTemplate(string message, string template)
{
    var templateParts = Regex.Split(template,"\\{.*?}")
    foreach (var templatePart in templateParts)
        Assert.IsTrue(message.Contains(templatePart));
}