我更喜欢从新功能的组件测试开始。组件测试是指服务的测试(在 SOA 术语中)连同它的适配器(shell 脚本、http 资源、gui 等),被视为一个黑盒子(你不知道里面是什么,你只知道入口接口和外部服务这取决于)。
在您的服务签名中,我们看到您希望从给定的 RSS 网址获取 RSS 新闻列表。假设该服务的适配器是一个简单的 http 控制器。
我将在这里使用伪代码,因为我不是 Java 本地人;)
function testThatListOfRSSNewsCanBeRetrieved() {
expectedNews = [
new News.with(new Title('Test title.')).with(new Content('Test content.')).build(),
new News.with(new Title('Test title 2.')).with(new Content('Test content 2.')).build()
];
rssFeed.addNews(expectedNews);
retrievedNews = applicationRunner.getRSSNews();
assertThat(retrievedNews, areSameAs(expectedNews))
}
好的,这很容易。如您所见,测试清晰易懂(我希望如此)。
首先我们使用构建器模式创建预期的新闻列表。
请注意,您不必这样做。您可以直接在此处直接使用域值对象。我喜欢使用构建器,因为当给定值对象的构造器发生更改时,我不必在所有测试中更改它,只需在构建器中更改即可。
在第二部分中,我们使用rssFeed 实例添加新闻。
rssFeed 是一个控制模拟外部 rss 服务器的对象。我们必须在测试之前启动该服务器。它只是服务于我们告诉它服务的东西。通过调用addNews,我们告诉它在请求时提供给定的新闻列表。有此类假服务器的库。使用这种方法,我们不会将服务实现与测试耦合,因为即使实现发生变化,测试仍然可以工作。
然后我们运行我们的应用程序并检查它是否按预期工作。
为了运行应用程序,我们使用包装器对象applicationRunner,它应该在单独的进程中运行应用程序(在这个例子中启动http服务器),向它发出请求,从响应中创建新闻并返回这些。
最后,我们断言检索到的新闻与我们之前准备的相同。
这就是组件测试的全部内容。请记住,您不应该创建太多这样的测试,因为它们很慢。通常一个,最多两个,足以覆盖一个功能。
这不是结束!
现在从您的组件测试开始,您可以计划服务实施并为其进行单元/集成测试。
我们希望我们的服务做的是从给定的 url 返回一个 RSS 新闻列表,这是我们的首要职责,将在服务层实现。让我们为此编写一个测试:
function testThatNewsListCanBeRetrieved() {
rssURL = 'http://megarss.com';
expectedNews = [new News.build()];
mockedRSSDataRetrieve = mock(RSSDataRetrieve());
mockedRSSDataRetrieve.on('get').withParameters(rssURL).returnValue(expectedNews);
service = new RSSNewsRetrieve(mockedRSSDataRetrieve);
newsList = service.newsList();
assertThat(newsList, areSameAs(expectedNews));
}
通过编写此测试的实现,我们可以提炼出两个新职责(请注意,我们可以在实现过程中发现新职责,这种情况经常发生并且没有错):
为了测试服务RSSNewsRetrieve,我们不得不模拟出RSSDataRetrieve,因为我们不想在单元测试中调用外部服务(单元测试应该独立且快速地运行)。 RSSResponseParse 不必被嘲笑甚至注入,因为它没有使我们无法孤立运行的依赖项。
现在我们可以为RSSResponseParser 编写单元测试,因为它很简单,所以我不会在这里介绍。
剩下要测试的是RSSDataRetrieve。我们将使用集成测试对其进行测试。
集成测试不需要单独运行,并且可能比单元测试慢一些。因为我们不想将测试与实现耦合并保持其确定性,所以我们可以再次使用为组件测试创建的假 RSS 服务器。
function testThatRSSDataCanBeRetrieved() {
expectedNews = [new News.build()];
rssFeed.addNews(expectedNews);
rssDataRetrieve = new RSSDataRetrieve();
rssData = rssDataRetrieve.get(this.url); // this url is passed to rssFeed on initialization
assertThat(rssData, isTheSameAs(newsToRawRSSData(expectedNews));
}
我没有为服务 HTTP 适配器编写单元测试,因为它包含在组件测试中。
通过这组测试,我们涵盖了保持高水平可读性和可维护性的功能基础。
当然,现在您可以针对不同情况添加更多测试,例如,如果 RSS 服务器不可用或 RSS 服务器以错误格式返回数据,应用程序应该如何运行。
我希望这会有所帮助。