下面是将要测试的代码, 将当前时间输出:
@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{ 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 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