- know Ruby
- know Rspec
- want another mocking mechanism for Ruby with Rspec
Let's say you have a game class that you are trying to test:
class Game def initialize(rules) @rules = rules end def over? return true if @rules.winner return true if @rules.tied? end end
This is what the Rules class looks like:
class Rules def initialize(board) @board = board end def tied? !winner && @board.filled? end def winner @board.unique_marked_values.detect {|p| win?(p)} end private def win?(player) square_sets.any? do |squares| squares.all? {|square| square == player} end end def square_sets @board.rows + @board.columns + @board.diagonals end endNote there are 4 methods in Rules, but only two are public, tied? and winner. Assuming you want to mock Rules because it is too complicated to set up a tied or winner situation.
- Create a mock folder in spec
- Create a mock file (you use the same name as your class file, in my case, rules.rb)
- In your mock file
- Require surrogate
- Endow the mock class
- Define the class methods
- Test if your mock is substitutable for the original class
#game/spec/game/rules_spec.rb require 'game/game' require 'game/rules' require 'mock/rules' describe Game do it "checks if MockRules is substitutable for rules" do MockRules.should be_substitutable_for(Rules) end end
- Use the mock class
#game/spec/mock/rules.rb require 'surrogate/rspec' class MockRules Surrogate.endow(self) define(:tied?) do false end define(:winner) end
3. Define your class method
There are three way to define your methods:
- One line with return type
- Three lines with return type
- One line with default return
define(:tied?) {false}
define(:tied?) do false end
define(:tied?)What this method really returns is "nil" and since nil evaluate to false as well, you can think of it as false.
def winner(board) ..... endthen in your mock class, you can define the method as follow:
define(:winner) do |value| value endor
define(:winner) {|value| value}
4. Test if the mock class is substitutable
This step is important because it ensure your class has the same methods/interface as the class you are mocking. This means if I add the method def name to Rules, when I run rspec, it will fail. Or if I decide to remove def winner in Rules, running the test will fail. Again, this restrict your MockRules to have only methods defined by Rules.
5. Use the mock class
Let's say you want to test that the game is over when there is a tied. Again, this is the game over method
def over? return true if @rules.winner return true if @rules.tied? return false endFor this test, you want to return true when rules.tied? is called and check to see if rules.winner is called. Here are the steps:
- Create a mock class with method you want to override
- Create the class under test
- Set the mock to game
- Call the method under test
- Verify expected behaviors
- Verify over? is true
- Verify winner was called
it "is over when the game is tied" do rules = MockRules.factory(tied?: true) #1. Create mock class with specified behavior game = Game.new(rules) #2. Create a game class 3. "Set" mock to game game.should be_over #4. Call method under test and 5. Verify expected behavior rules.was asked_for :winner #5. Verify expected behavior endNote, some say that you should only test for one behavior in each test. I have not figure out where I stand on this argument, but this post is about using surrogate so it's easier for you to read if I put everything under one test. Surrogate can do a lot more. This is just a basic overview on how to use it.
Resource
0 comments:
Post a Comment