TDD:您将如何以测试优先的方式开展这门课程?
我正在编写一个小应用程序来自学 ASP.NET MVC,它的功能之一是能够在 Amazon(或其他网站)上搜索书籍并将它们添加到“书架”中。
因此,我创建了一个名为 IBookSearch 的接口(带有 DoSearch 方法),以及一个看起来像这样的 AmazonSearch 实现。
public class AmazonSearch : IBookSearch
{
public IEnumerable<Book> DoSearch(string searchTerms)
{
var amazonResults = GetAmazonResults(searchTerms);
XNamespace ns = "http://webservices.amazon.com/AWSECommerceService/2005-10-05";
var books= from item in amazonResults.Elements(ns + "Items").Elements(ns + "Item")
select new Book
{
ASIN = GetValue(ns, item, "ASIN"),
Title = GetValue(ns, item, "Title"),
Author = GetValue(ns, item, "Author"),
DetailURL = GetValue(ns, item, "DetailPageURL")
};
return books.ToList();
}
private static XElement GetAmazonResults(string searchTerms)
{
const string AWSKey = "MY AWS KEY";
string encodedTerms = HttpUtility.UrlPathEncode(searchTerms);
string url = string.Format("<AMAZONSEARCHURL>{0}{1}",AWSKey, encodedTerms);
return XElement.Load(url);
}
private static string GetValue(XNamespace ns, XElement item, string elementName)
{
//Get values inside an XElement
}
}
理想情况下,我希望完成这种 TDD 风格,首先编写测试等。 但我必须承认我很难理解它。
我可以创建一个实现 DoSearch() 的 FakeSearch 并返回一些临时书籍,但我认为目前这不会带来任何价值,不是吗? 也许稍后当我有一些使用书籍列表的代码时。
我还可以先测试什么? 我能想到的唯一测试是模拟对云的调用(在 GetAmazonResults),然后检查 DoSearch 是否可以正确执行 Linq2XML 选择并返回正确的列表。 但在我看来,这种类型的测试只能在我编写了一些代码之后才能编写,这样我就知道要模拟什么。
对于你们如何采用这种测试优先的风格有什么建议吗?
I am writing a small app to teach myself ASP.NET MVC, and one of its features is the ability to search for books at Amazon (or other sites) and add them to a "bookshelf".
So I created an interface called IBookSearch (with a method DoSearch), and an implementation AmazonSearch that looks like this
public class AmazonSearch : IBookSearch
{
public IEnumerable<Book> DoSearch(string searchTerms)
{
var amazonResults = GetAmazonResults(searchTerms);
XNamespace ns = "http://webservices.amazon.com/AWSECommerceService/2005-10-05";
var books= from item in amazonResults.Elements(ns + "Items").Elements(ns + "Item")
select new Book
{
ASIN = GetValue(ns, item, "ASIN"),
Title = GetValue(ns, item, "Title"),
Author = GetValue(ns, item, "Author"),
DetailURL = GetValue(ns, item, "DetailPageURL")
};
return books.ToList();
}
private static XElement GetAmazonResults(string searchTerms)
{
const string AWSKey = "MY AWS KEY";
string encodedTerms = HttpUtility.UrlPathEncode(searchTerms);
string url = string.Format("<AMAZONSEARCHURL>{0}{1}",AWSKey, encodedTerms);
return XElement.Load(url);
}
private static string GetValue(XNamespace ns, XElement item, string elementName)
{
//Get values inside an XElement
}
}
Ideally I would like to have done this TDD-style, writing first a test and all. But I gotta confess I am having trouble getting my head around it.
I could create a FakeSearch that implements DoSearch() and return some ad-hoc books, but I don't think that brings any value at the moment, does it? Maybe later when I have some code that uses a list of books.
What else could I test first? The only test I can think of would be one that mocks the call to the cloud (at GetAmazonResults) and then checks that DoSearch can execute the Linq2XML select correctly and return the correct list. But it seems to me that this type of test can only be written after I have some code in place so I know what to mock.
Any advice on how you guys and girls would go around doing this test-first style?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
看来您这里的主要问题是知道何时编写模拟代码。 我明白你的观点:如果你还没有编写代码,你怎么能模拟它呢?
我认为答案是您希望通过非常非常简单的测试开始 TDD,正如 Kent Beck 在 测试驱动开发。 首先编写一个测试,调用 DoSearch 并断言您收到的内容不为空,然后编写一些代码来完成该测试。 然后编写一个测试,断言您正在为已知搜索词检索正确数量的书籍,并编写代码来完成该测试。 最终,您将到达需要接收实际、有效的 Book 数据才能通过测试的地步,此时,您将编写 DoSearch 的一部分,并且您可以考虑模拟它(或其中的一部分) )。
It seems that your main issue here is knowing when to write mock code. I see your point: if you haven't written the code yet, how can you mock it?
I think the answer is that you want to start your TDD with very, very simple tests, as Kent Beck does in Test Driven Development. Start by writing a test that calls DoSearch and asserts that what you receive isn't null, and write some code to make that pass. Then write a test that asserts that you're retrieving the proper number of Books for a known search term, and write the code to make that pass. Eventually you'll get to a point where you need to receive actual, valid Book data to pass a test, and at that point, you'll have a portion of DoSearch written, and you can think about mocking it (or portions of it).
当您测试使用搜索的代码时,您需要编写一个模拟,而不是测试搜索本身。
对于上面的课程,我可能会通过以下方式进行测试:
但是..这是一个大问题,我永远不会模拟我正在测试的类,我会模拟它使用的类。
长话短说:在测试按下“搜索”按钮时 UI 是否正常工作时,将使用 FakeSearch。 我可以确保它被调用,并且 UI 正确处理返回的书籍。
希望有帮助。
You'll want to write a mock when you're testing code that uses the search, not for testing the search itself.
For the class above, I might test by:
But.. and here's the big one, I'd never mock out a class I was testing, I'd mock out classes that it used.
Long story short: FakeSearch would be used when testing that the UI was working properly when the Search button was pressed. I could ensure that it was getting invoked, and that the UI was handling the returned books properly.
Hope that helps.
在本课程中,主要焦点似乎是它与亚马逊的网络服务正确集成。 由于该 Web 服务不是您拥有的东西,因此您不应该嘲笑它,因为您对它的工作原理并不了解。 “仅您拥有的模拟类型” , “不要模拟第三方库” 等。
以下是解决该问题的一些方法:
编写一个通过网络连接到真实 Web 服务的测试,也许可以搜索一些您喜欢的非常流行的书籍可以信任将在未来几年内继续存在。 这可以很好地保证您正确使用该服务,但它也会受到许多误报的影响 - 例如,有时网络可能会关闭,或者远程系统中的数据会发生变化。 因此,您还需要测试...
针对静态数据文件编写测试,这些测试基于来自真实 Web 服务的数据。 要获取测试数据,您可以手动向 Web 服务发出请求并将响应写入文件*。 您需要模拟网络连接(使用不联网的存根,或者在测试中启动嵌入式 Web 服务器并连接到它而不是真实的 URL)。 通过这种方式,您可以轻松测试各种极端情况和错误条件,并且无论真实的 Web 服务发生什么情况,数据都将始终可用并保持不变。 需要注意的是,如果真实 Web 服务的 API 发生变化,这些测试将不会注意到它,因此您还需要针对真实 Web 服务编写一些测试(如上所述)。
* 例如,有一次我使用 cron 和一个小 shell 脚本从 Web 服务每隔几分钟下载一次数据,其中包含不断变化的时间表信息。 在几周内收集此类数据作为测试数据非常有用。 根据这些数据,我手工制作了静态响应,其中包含我在真实数据中注意到的各种特殊情况。 它对于设置一个假 Web 服务和一个“时间机器”也很有用,它可以重放之前捕获的数据,这样我们的系统就可以在不访问真实 Web 服务的情况下使用。
In this class the main focus appears to be that it integrates correctly with Amazon's web services. Since that web service is not something you own, you shouldn't mock it, because you don't have intimate knowledge of how it works. "Only mock types you own", "don't mock third-party libraries" etc.
Here are some ways to approach the problem:
Write a test which connects to the real web service over the network, perhaps searching for some very popular book which you can trust will be around for years to come. This gives good assurance that you are using the service correctly, but it's also subject to many false positives - for example sometimes the network might be down or then the data in the remote system changes. Thus you will also need tests which...
Write tests against static data files, which are based on data from the real web service. To get the test data, you could manually do requests to the web service and write the responses to file*. You will need to mock the network connection (either using a stub which does no networking, or by starting up an embedded web server in the tests and connecting to it instead of the real URL). This way you can easily test all kinds of corner cases and error conditions, and the data will always be available and stay the same, regardless of what happens to the real web service. One caveat is that if the API of the real web service changes, these tests will not notice it, so you will also need some tests written against the real web service (as mentioned above).
* For example, once I used cron and a little shell script to download every few minutes data from a web service, which contained ever changing schedule information. Gathering such data for a period of a few weeks was very useful as test data. From that data I hand-crafted static responses which contained all kinds of special cases which I noticed in the real data. It was also useful for setting up a fake web service and a "time machine" which replayed that earlier captured data, so that our system could be used without access to the real web service.