[对谈录]是否可以把logger的输出视为一个方法的output, 并针对它进行单元测试?
A:logger的输出和一个方法的return, exception, output parameter一样,都可以视为这个方法的输出。如果logger的输出的确代表了功能需求,那我们就应该去测试它,包括单元测试(比如用程序读log文件然后与expectation比较)。典型的用况是批量操作中部分失败的日志。按需求,如果部分失败,不能抛exception,而应该把部分的失败放到日志中去。
B:log是跟业务相去甚远的东西,不应该把它当作模块的一种输出,因此不能做单元测试;至于前面提到的批量操作的例子,应该把日志将要打印的错误信息置为单个操作的返回值或者异常消息,然后只针对批量中的单个操作进行单元测试。只要单个操作的测试通过了,没必要再对批量操作施加日志相关的测试了。
A: 但是批量操作的日志可能不仅是各个单步操作的简单叠加。它的日志可能跟批量操作的上下文有关,而单步操作的方法可能不方便访问这个上下文;又或者批量操作输出的日志可能跟若干单步操作结果的组合有关,针对单步操作的方法无法进行这种测试。
B: 那批量操作的代码就应该重构了。应该把所有的出错信息作为return值或output parameter的值,输出来,再用单元测试的代码去验证它。
A:这似乎是比较优雅的方案。它不但解决了我们的争论,而且还使得log代码不能入侵到业务逻辑之中,使业务逻辑更干净。不过,这可能会存在性能问题。如果一个批量操作中有十几万条错误信息,把这些错误信息放到一个变量里,可能会导致内存溢出。如果用log来做,就没有这个问题,因为它见一条处理一条,不会产生巨大的对象。
B:...
A:...
噢。这样子啊。
之所以要有单元测试,是为了能尽早的发现问题,随时修正航向。
根据(落后的)瀑布模型,如果程序都开发完了,才进行测试,到时候才发现问题可能很不好改。更严重的,可能发现已经偏离轨道很远了(做的根本就不是想要的)。
从这个角度来说,日志内容方面的错误,等最后功能全开发完了再检查也不迟吧?需要特意花时间写单元测试来得到快速的反馈么?(而且这种单元测试对指导设计方面也没有什么帮助)
import org.junit.Test;import java.util.logging.*;class MyLogHandler extends Handler{ private LogRecord record;@Overridepublic void flush() {}@Overridepublic void close() throws SecurityException {}public void publish(LogRecord record) {this.record = record;}public String getMessage() {return record.getMessage();}} class MyTemp {static Logger logger = Logger.getLogger(MyTemp.class.getName()); public MyTemp(){ MyTemp.logger.info("abcdefg"); }}public class MyTest {@Testpublic void testLogMessage() {Handler h = new MyLogHandler();MyTemp.logger.addHandler(h);MyTemp.logger.setLevel(Level.FINEST);// 这个设定最重要MyTemp t = new MyTemp();System.out.println(((MyLogHandler) h).getMessage());} }
当然日志他只把最近的日志打出来... 29 楼 wl95421 2009-04-29 中文版本正在翻译中,估计是下半年出版。
不过感觉书对程序员水平有一定要求,估计销量可能会一般。