C# 如何在模拟不可用时保持单元测试干燥';不行?
编辑: 似乎通过尝试为我自己的问题提供一些解决方案,我模糊了整个问题。所以我对这个问题稍加修改 假设我有这个班:C# 如何在模拟不可用时保持单元测试干燥';不行?,c#,.net,unit-testing,tdd,mocking,C#,.net,Unit Testing,Tdd,Mocking,编辑: 似乎通过尝试为我自己的问题提供一些解决方案,我模糊了整个问题。所以我对这个问题稍加修改 假设我有这个班: public class ProtocolMessage : IMessage { public IHeader GetProtocolHeader(string name) { // Do some logic here including returning null // and throw exception in some
public class ProtocolMessage : IMessage
{
public IHeader GetProtocolHeader(string name)
{
// Do some logic here including returning null
// and throw exception in some cases
return header;
}
public string GetProtocolHeaderValue(string name)
{
IHeader header = GetProtocolHeader(name);
// Do some logic here including returning null
// and throw exception in some cases
return value;
}
}
实际上,这些方法中发生了什么并不重要。重要的是我有多个单元测试来覆盖GetProtocolHeader
方法,覆盖所有情况(返回正确的头、null或异常),现在我正在为GetProtocolHeaderValue
编写单元测试
如果GetProtocolHeaderValue
依赖于外部依赖,我将能够模拟它并注入它(我使用Moq+NUnit)。然后,我的单元测试将只测试调用外部依赖项并返回预期值的预期。外部依赖将由它自己的单元测试来测试,我也会这样做,但是在这个示例中,如果方法不是外部依赖,如何正确地继续
澄清问题:
我相信我的
GetProtocolHeaderValue
测试套件必须测试GetProtocolHeader
返回header、null或异常的情况。因此,主要的问题是:我是否应该编写真正执行GetProtocolHeader
的测试(一些测试将被复制,因为它们将测试与GetProtocolHeader
本身相同的代码)或者我应该使用@adrift和@Eric Nicholson所描述的模拟方法吗?在这里,我不会运行realGetProtoclHeader
,而只是将模拟配置为在调用此方法时返回header、null或exception?我经常使用部分模拟(在Rhino中)或等效方法(如Fakeitesy中的CallsBaseMethod)模拟我正在测试的实际类。然后,您可以使GetProtocolHeader虚拟并模拟对它的调用。你可能会说它违反了单一责任原则,但这显然仍然是非常内聚的代码
或者,您可以使用如下方法
internal static string GetProtocolHeaderValue(string name, IHeader header )
并独立测试该处理。公共GetProtocolHeaderValue方法不会有任何/许多测试
编辑:在这个特定的情况下,我还将考虑将GETValueAd()作为IHeader的扩展方法添加。这将非常容易阅读,并且您仍然可以执行空检查。
我可能遗漏了一些内容,但是根据列出的代码,您似乎不需要担心它是否被调用 存在两种可能性:GetProtocolHeader()
方法需要是公共的,在这种情况下,您可以编写一组测试来告诉您它是否按预期工作GetProtocolHeaderValue()
是否按需工作在这两种情况下,您都在测试公开的功能,而在一天结束时,这才是最重要的。如果它是一个依赖项,那么是的,您可能会担心它是否被调用,但如果它不是唯一的依赖项,那么它肯定是一个实现细节,并且不相关?在调用
GetProtocolHeaderValue
时,您是否确实需要知道它是否被调用GetProtocolHeader
当然,知道它从正确的头中获得正确的值就足够了。它实际上是如何得到它的与单元测试无关
您正在测试功能单元,GetProtocolHeaderValue
的功能单元是给定标题名时是否返回预期值
确实,您可能希望防止不适当的缓存、交叉污染或从不同的头获取值,但我不认为测试它调用了GetProtocolHeader
是最好的方法。您可以推断,它以某种方式获取了正确的头,因为它返回了头的预期值
只要您以这样的方式精心设计您的测试和测试数据,以确保重复的头不会掩盖错误,那么一切都应该很好
编辑更新的问题:
GetProtocolHeader
能够快速、可靠地工作并且是幂等的,那么我仍然认为没有必要对它进行模拟。(国际海事组织)这三个方面的不足是嘲弄的主要原因
如果(正如我从问题标题中怀疑的那样),您希望模拟它的原因是设置适当状态以返回实际值所需的序言太冗长,并且您不希望在两个测试中重复它,为什么不在设置阶段进行呢GetProtocolHeader
在name为空时抛出异常。相应地创建一个模拟,并确保GetProtocolHeaderValue
适当地处理该异常。稍后,您决定GetProtocolHeader
应该为空名称返回null
。如果忘记更新mock,GetProtocolHeaderValue(“”)
现在在现实生活中的行为将与测试套件不同GetProtocolHeaderValue
需要测试的三个不同的GetProtocolHeaderValue
响应(header、null或异常),但我认为第一个响应可能是“一系列头”。(例如,我应该做什么
public string GetProtocolHeaderValue(string name)
{
IHeader header = GetProtocolHeader(name);
return RetrieveHeaderValue(IHeader header);
}
public string GetProtocolHeaderValue(string name, IHeaderParser parser)
{
IHeader header = parser.GetProtocolHeader(name);
return parser.HeaderValue(IHeader header);
}