
| Mocking and stubbing in Ruby on Rails | |||||||||||||||||||||||||||||||||||||||||||||||
| 摘自: IBM developerWorks Worldwide 被阅读次数: 66 | |||||||||||||||||||||||||||||||||||||||||||||||
由 yangyi 于 2007-12-17 19:17:39 提供 | |||||||||||||||||||||||||||||||||||||||||||||||
Level: Intermediate Bruce Tate (bruce@rapidred.com), CTO, WellGood LLC 07 Nov 2007 Understand the basic foundations behind stubbing and mocking techniques and strategies with this hands-on walkthrough using the three most popular mocking frameworks for Ruby. After publishing my last article on RSpec, several readers approached me and asked what mock objects might look like in RSpec. I must confess that I deliberately dodged the subject of mocking frameworks last time. The RSpec team has had some internal discussion about whether to continue to invest in building and maintaining a mocking framework or to adopt one of the existing frameworks. In this article, I'll walk you through the basics of stubbing and mocking. Then, I'll show you examples of both using three popular Ruby frameworks. Nearly all strategies for testing automation depend on some fundamental concepts. Tests need to be:
Good programmers look for ways to substitute slow, unpredictable, or complicated pieces of an application for these reasons. After accumulating years of experience, the industry has settled on at least two important techniques to replace different pieces of a system, much like a builder might build temporary scaffolding to support a structure during construction. Almost four years ago, Martin Fowler of ThoughtWorks published a famous blog post called Mocks Aren't Stubs (see Resources for a link). Four years later, most developers still get the concepts confused. (On a side note, maybe we've picked some poor names to describe the concepts, but we're stuck with them now.) Let me clarify the concepts with an analogy. Every Christmas, my wife and I get out the Christmas tree and decorate it. Raising the tree should be a cherished memory, but for me, it is a nightmare. I have to deal with the lights. Every year, a handful of lights shake loose, taking out a whole strand of lights. In practice, the job is simple. I have to find the bad bulb. I just work through the lights on the strand that is out, testing each one. I can test each one in at least two different ways. These techniques are like stubbing and mocking.
Stubbing
Now, think of that bulb as a class or component. The client code that uses the object is the light strand. The socket is the interface. Stubbing preserves the interface but replaces some or all of the client code for the object under test.
Mocking
A database stub might respond to a query by returning fake records. A
mock would do that too, but a mock database object might also make sure
the client code called certain queries and ultimately called Stubbing and mocking are powerful techniques that can improve the speed of your test cases, isolate your code, simplify your tests, and—some believe—solve world hunger. You know there's always a big 'but.' With mocking and stubbing, you have to be careful not to replace too much of the real world. I've worked with many a client who has ultimately stubbed out anything resembling the real world. Keep in mind that at some point, you need to test that nasty database code with the rest of the application. Consider yourself warned.
When you're building any stub for any framework, your approach is basically the same. You have to do two things:
You might decide to replace a whole object or simply change the results of a single method. When you're replacing a whole object, it's easy—just write the new object and replace it within your test case. Methods might be tougher in some languages, but using dynamic languages such as Ruby, stubbing is easy because you're simply redefining a method. Whew. This theory is getting a little too abstract. It's time to bring these ideas down to earth. Imagine you have a user interface that prints a document. You want to test code that handles a print failure. You really don't care if the print method gets called. You just want to verify that if your code does call print, that code will handle a failed print effectively. Think of what a pseudo-code sentence would look like. On an instance of the document object, stub the print method, returning false. You can break that sentence down into two parts. The first part identifies what you want to stub. The second part defines what the stub should do. Now, it's time to build that stub using three frameworks. Right now, one of the top mocking and stubbing frameworks is Mocha. You can install it easily through gems: Listing 1. Installing Mocha
To demonstrate it, I'm going to need an application. My application in
Listing 2 will consist of a model called Listing 2. The application in document.rb
I'm not going to bother to build more than a shell for a Listing 3 shows the test case. Listing 3. Testing with Mocha
You can see how this works. I build a simple, named stub object with the
statement On an instance of the document object, stub the print method, returning false. I can simplify the first two lines of the test method into one: Listing 4. Simplifying the test
The version in Listing 4 substitutes the stub for Along with Mocha, Flex Mock is a popular mocking framework. Jim Weirich, the popular author of everyday Ruby tools such as rake, wrote Flex Mock to handle basic mocking and stubbing in Ruby tests. In terms of popularity and utility, Mocha and Flex Mock are very similar. Installing Flex Mock is easy with gems (another Jim Weirich project): Listing 5. Installing Flex Mock
You can create a stub with the Listing 6. Stubbing with Flex Mock
The syntax is a little different, but the concepts are exactly the same.
You're replacing Keep in mind that you can use both Mocha and Flex Mock with RSpec. That said, the syntax for creating a stub with RSpec is similar to Mocha. Listing 7 shows how. Listing 7. Stubbing with RSpec
The syntax is strikingly similar to Mocha's syntax. Your main decision with RSpec is whether to use an API that could become deprecated soon or add another testing framework to RSpec. Right now, one of the nice things about RSpec is that it is a one-stop shop for testing. RSpec handles all of the major concerns. But when you look at the syntax of Mocha and RSpec side by side, you'll agree that you don't lose much by using Mocha within RSpec test cases.
As you've learned, creating a mock object is much like creating a stub. The difference is that a stub is passive—it merely simulates the real-world solution you invoke for stubbed methods. A mock is active—it actually tests the way that you use the mock object. If you don't use it in a way that matches your expectations, your test will fail. These are the basic steps to using a mock:
Think back to the voltmeter and the Christmas lights. I took out a bulb, which represents the object under test. I then replaced the strand of lights that represented the real-world application with the voltmeter that represented the object under test. I should define two implicit steps. I used a voltmeter because I expected the needle to register current if the bulb worked. After the test, I looked at the voltmeter. These implicit steps represent Step 3 and Step 4. In practice, the testing framework will usually handle Step 4 after the test case completes. A line of pseudo-code describing a mock object for my document printing application might look like this: For a Mocking with Mocha, Flex Mock, and RSpec Often, when you can express an idea in a single sentence, you have a good shot at implementing that idea in a single line or two of Ruby code. Listing 8 shows the incredibly simple Mocha code to change the stub in Listing 4 into a mock. Listing 8. Simplifying the test
I changed the method The Flex Mock code to implement mocking is nearly identical to the Mocha version, though the method names are a little different. Listing 9 shows the difference. Listing 9. Mocking with Flex Mock
And Listing 10 shows the same in RSpec: Listing 10. Mocking with RSpec
You can add quite a few qualifiers to these mocking frameworks. You can specify expectations for the calling parameters and the number of invocations. You can stub and mock full objects, a single method on an object, or class methods. I'd suggest that you use Flex Mock or Mocha for mocking, at least until discussions about deprecation settle down a little.
If you've been active in Ruby for the last two years, you've noticed a huge investment in advanced testing ideas. Some testing tools have been around for a long time, but now the masses are starting to take notice. As Ruby enthusiasts develop more test cases, techniques like mocking will become more commonplace. Stubbing and mocking are invaluable techniques to all sorts of Ruby coders. Test first developers will appreciate the ability to mock interfaces that are not fully developed. Programmers with slow test suites can use stubs and mocks to remove expensive interfaces. All testers must use these techniques to add predictability to tests. Rather than show you an exhaustive review of the mocking libraries in Ruby, I've shown you the concepts behind mocking. I've also given you a quick review of the three most popular mocking libraries. If these concepts are new to you, you still have much to learn, but you should have a sound foundation for further exploration. As always, the best way to learn more is to get coding! Learn
Get products and technologies
Discuss
原文链接: http://www.ibm.com/developerworks/library/wa-mockrails/?S_TACT=105AGX54&S_CMP=A1109&ca=dnw-843 | |||||||||||||||||||||||||||||||||||||||||||||||
