假设我有一个类,其方法在某种条件下返回错误消息,如下所示:
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
)的消息类,但这感觉......详细。建议?
答案 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));
}