我们公司有一个习惯,即当报告错误时,我们会执行以下步骤:
现在我遇到了一段非常简单的遗留代码。情况如下:
public final class SomeClass {
...
public void someMethod(Parameter param) {
try {
if (param.getFieldValue("fieldName").equals("true")) { // Causes NullPointerException
...
}
} catch (Exception ex) {
log.warn("Troubles ...", ex);
}
}
}
这里的问题是fieldName
不是强制性的,所以如果不存在,你会获得NPE。显而易见的解决方法是:
if ("true".equals(param.getFieldValue("fieldName"))) {
...
}
我的问题是如何编写单元测试以使方法失败。如果我传入一条不包含fieldName
的消息,它只会记录NPE,但不会失败......
您可能会想到该方法的作用是什么?我可以测试该方法的效果。不幸的是,它与一些远程系统进行通信,因此这将需要一个巨大的集成测试,这对于这样一个小而且前瞻性的bug来说似乎有些过分。
请注意,如果不是不可能在代码中进行任何不直接导致错误的更改,那将非常困难。因此,更改代码以使其更容易测试可能不是一种选择。这是非常可怕的遗留代码,每个人都非常害怕触摸它。
答案 0 :(得分:2)
你可以做几件事:
答案 1 :(得分:2)
我认为最好的做法是模拟记录器,并断言记录器已经不调用了传递。如果这是一个很大的变化,我假设记录器在很多地方使用,这将有助于您将来进行其他测试。对于快速修复,您可以在异常捕获器中引发一个事件,但我不认为这是一种非常“干净”的方式。
答案 2 :(得分:0)
catch (ExpectedException ex)
答案 3 :(得分:0)
log
可以是注入服务,这意味着SomeClass
看起来像这样:
public final class SomeClass
{
private final Logger log;
public SomeClass(Logger log)
{
this.log = log;
}
}
因此,在您的单元测试中,您可以传递假Logger
(可能使用mocking framework构建),以便您可以检测测试期间是否记录了警告。
如果log
是全局变量,则可以使该全局变量可写,以便您可以以类似的方式替换测试中的记录器。
另一种选择是在单元测试中添加Handler
到log
,以便检测warn
次来电(假设您正在使用java.util.logging.Logger,但是似乎并非如此,因为该方法是warning,而不是warn
)。
答案 4 :(得分:0)
嗯,你的公司方法确实意味着你有时候必须编写很多额外的测试,你可以争论这是否是这种“明显容易出错”的方法。但是你这样做是有原因的。
我会这样看:
因此,您可能应该编写整个代码,即使使用外部系统,也可以证明函数有效。或者在这种情况下,不会。
答案 5 :(得分:0)
在测试开始时为此类/记录器添加警告级别的其他记录器appender(并在结束时将其删除)。
然后运行someMethod
并检查新的appender是否仍为空(然后没有异常)或有内容(然后有异常)。
答案 6 :(得分:0)
您可以考虑使用Boolean.toBoolean(String)或“true”.equalsIgnoreCase(text)
如果您不想要此行为,则可能需要添加一个测试,该测试显示“True”和“TRUE”被视为false。