通过下面的例子了解Guava Supplier的用法.在做单元测试的时候, 我们可能需要Mock掉一些对外部资源的依赖. 比如时间, 随机数, 系统文件访问.
下面是将要测试的代码, 将当前时间输出:
@Controller
@RequestMapping(value = "/time")
@VisibleForTesting
class TimeController {
@RequestMapping(value = "/current", method = RequestMethod.GET)
@ResponseBody
public String showCurrentTime() {
// BAD!!! Can't test
DateTime dateTime = new DateTime();
return DateTimeFormat.forPattern("hh:mm").print(dateTime);
}
}
但是这里有一个问题, 就是代码中是直接new的一个DateTime对象, 所以造成了对系统时间的直接依赖. 导致我们对输出时间的测试比较困难, 因此这里我们要借助Guava Supplier.
Supplier只有一个方法: get(), 返回suppiler构建的对象. 比如下面的例子:
public class FirstNameSupplier implements Supplier<String> {
private String value;
private static final String DEFAULT_NAME = "GUEST";
public FirstNameSupplier() {
// Just believe that this goes and gets a User from somewhere
String firstName = UserUtilities.getUser().getFirstName();
// more Guava
if(isNullOrEmpty(firstName)) {
value = DEFAULT_NAME;
} else {
value = firstName;
}
}
@Override
public String get() {
return value;
}
}
通过上面的代码你将不再关心firstName是什么, 只需要调用get方法即可.
对目标代码进行重构
实现一个DateTime Supplier. 同时提供一个接口, 方便测试:
public interface DateTimeSupplier extends Supplier<DateTime> {
DateTime get();
}
下面是具体实现
public class DateTimeUTCSupplier implements DateTimeSupplier {
@Override
public DateTime get() {
return new DateTime(DateTimeZone.UTC);
}
}
然后注入DateTimeSupplier:
@Controller
@RequestMapping(value = "/time")
@VisibleForTesting
class TimeController {
@Autowired
@VisibleForTesting
// Injected DateTimeSupplier
DateTimeSupplier dateTime;
@RequestMapping(value = "/current", method = RequestMethod.GET)
@ResponseBody
public String showCurrentTime() {
return DateTimeFormat.forPattern("hh:mm").print(dateTime.get());
}
}
创建一个MockDateTimeSupplier用来做测试:
public class MockDateTimeSupplier implements DateTimeSupplier {
private final DateTime mockedDateTime;
public MockDateTimeSupplier(DateTime mockedDateTime) {
this.mockedDateTime = mockedDateTime;
}
@Override
public DateTime get() {
return mockedDateTime;
}
}
最后是测试代码:
public class TimeControllerTest {
private final int HOUR_OF_DAY = 12;
private final int MINUTE_OF_DAY = 30;
@Test
public void testShowCurrentTime() throws Exception {
TimeController controller = new TimeController();
// Create the mock DateTimeSupplier with our known DateTime
controller.dateTime = new MockDateTimeSupplier(new DateTime(2012, 1, 1, HOUR_OF_DAY, MINUTE_OF_DAY, 0, 0));
// Call our method
String dateTimeString = controller.showCurrentTime();
// Using hamcrest for easier to read assertions and condition matchers
assertThat(dateTimeString, is(String.format("%d:%d", HOUR_OF_DAY, MINUTE_OF_DAY)));
}
}
总体感觉代码还是比较多的, 又是接口又是实现的. 而目标就是为了对系统时间进行mock, 不过提供了一种单元测试的思路.
原文:
http://java.dzone.com/articles/mocking-jodatimes-datetime-and