Mocking Facebook

December 10th, 2007

Any sort of interaction with external services is a serious hassle when trying to do TDD or even Development with Some Tests In It. External services could include anything from web services like facebook to our own drb ferret service (the real purists would include your database in this, but one step at a time…). The worst thing to do is to actually interact with the service in the test—it makes tests slow, dependent on a network connection, potentially messes up production data, etc.

The solution is mock objects. For a long time I did mock objects the wrong way, by inheriting from the real object and essentially reimplementing the functionality. This is pretty painful, so I didn’t do it much. Fortunately, really good mocking frameworks are everywhere now. Ruby even has two of them—mocha and flexmock.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

    def test_facebook_publish_complete_goal
      @mock_fbsession = valid_facebook_session
      
      person = people(:rob_cooper)
      person.facebook_session = @mock_fbsession

      @mock_team_member = flexmock
      @mock_team_member.should_receive("goal.name").and_return("write a facebook app")
      @mock_team_member.should_receive("goal_is_complete?").and_return(true)
      @mock_team_member.should_receive("give_up?").and_return(false)

      @mock_fbsession.should_receive(:feed_publishActionOfUser).
        with(:title => 'has completed the goal: write a facebook app').once
      
      person.facebook_publish_goal_activity(@mock_team_member)
    end

First some preliminaries

First I need to set up a mock FacebookSession (I use a pseudomock wrapped around a real FacebookWebSession because I’m lazy and don’t want to mock the session_id and session_key accessors). The RFacebook library obviously wasn’t implemented TDD, so there are a few ways that it’s a bit messy to test.

Hide the description

1
2
3
4
5
6
7
8

    def valid_facebook_session
      mock_session =  flexmock(RFacebook::FacebookWebSession.new("test", "test"))
      mock_session.should_receive(:is_valid?).and_return(true)
      mock_session.should_receive(:is_ready?).and_return(true)

      return mock_session
    end

Next, I’m making a mock team member (a TeamMember is a model that represents a Person doing a Goal), but this time I use a plain mock that doesn’t reference the “real” object at all. And check out this line:
1
2

      @mock_team_member.should_receive("goal.name").and_return("write a facebook app")

This call is actually making a chain of mock objects, so I can call @mock_team_member.goal.name without having to explicitly create a mock object for the goal.

Here’s the guts

The first line sets up the expectation—kind of like an assert.

1
2
3
4
5

      @mock_fbsession.should_receive(:feed_publishActionOfUser).
        with(:title => 'has completed the goal: write a facebook app').once
      
      person.facebook_publish_goal_activity(@mock_team_member)

The second line calls the method we wants to test. The expectation (that the method feed_publishActionOfUser will be called once with the specified arguments) will magically be evaluated when the test is over.

2 Responses to “Mocking Facebook”

  1. Vitor Pellegrino Says:
    Thanks for such an useful article! One thing i would like to know is if you have already done some work about developing an app using BDD with rSpec and rFacebook. I have some problems in testing stuff related to rfacebook. Thanks in advance!
  2. Laurel Fan Says:
    Vitor, I haven't tried using rfacebook with rSpec. I just came across a new ruby/facebook user group which might be a good place to ask: http://groups.google.com/group/rfacebook

Sorry, comments are closed for this article.