4

我正在编写一个小应用程序来自学 ASP.NET MVC,它的一个功能是能够在亚马逊(或其他网站)搜索书籍并将它们添加到“书架”。

所以我创建了一个名为 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 并返回一些临时书籍,但我认为目前这不会带来任何价值,不是吗?也许稍后当我有一些使用书籍列表的代码时。

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?

4

3 回答 3

3

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).

于 2009-01-16T19:03:58.507 回答
2

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:

  • searching for a common book and checking that is was found and is valid.
  • searching for a random fixed string "kjfdskajfkldsajklfjafa" and making sure no books were found
  • etc

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.

于 2009-01-16T19:26:14.203 回答
0

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.

于 2011-06-16T21:15:44.447 回答