views:

90

answers:

6

Hi there,

I just recently read about "Mocking objects" for unit testing and currently I'm having a difficulties implementing this approach in my application. Please let me explain my problem.

I have a User model class, which is dependent on 2 data sources (database and facebook web service). The controller class simply use this User model as an interface to access data and it doesn't care about where the data came from.

Currently I never done any unit test to this User model because it is dependent on an external web service. But just a while ago, I read about object mocking and now I know that it is a common approach to unit test a class that depends on external resources (like in my case).

Now I want to create a unit test for the User model, but then I encountered a design issue: In order for the User model to use a mocked Facebook SDK, I have to inject this mocked Facebook SDK to the User object (probably using a setter). Therefore I can't construct the Facebook SDK inside the User object. I have to construct it outside the User object, and inject the SDK into the User object.

The real client of my User model is the application's controller. Therefore I have to construct the Facebook SDK inside the controller and inject it to the user object. Well, this is a problem because I want my controller to be as clean as possible. I want my controller to be ignorant about the application's data source.

I'm not good at explaining something systematically, so you'll probably sleeping before reading this last paragraph. But anyway, I want to ask if anyone here ever encountered the same problem as mine? How do you solve this problem?

Regards, Andree

P.S: I'm using Zend framework, PHP 5.3.

A: 

If you make the Facebook SDK Object(s) publicly accessible, your User object can still create it when it sets up but you can replace it with a mock before you actually do anything on the User object.

[Test]
public void Test()
{
    User u = new User(); // let's say that the object on User gets created in the ctor
    u.FacebookObj = new DynamicMock(typeof(FacebookSDK)).MockInstance;

    Assert.That(u.Method(), Does.Stuff, "u.Method didn't do stuff");
}
SnOrfus
A: 

You don't say what language you are using, but I use Ruby and Mocha for mocking objects, and it's quite easy.

See http://mocha.rubyforge.org/.

here the fourth example shows that any call to Product.name from the unit under test is intercepted and the value 'stubbed_name' is returned.

I guess there are similar mechanisms in Java, etc.

stephenr
A: 

Since you will be Unit Testing your test class will play the role of the Controller, so that one should not be touched. So that can remain clean.

You are well underway of reinventing Dependency Injection. So it may be worthwhile to look at Spring or Guice to help you with the plumbing. (If you are in Java land).

Your test can indeed do the injection of the Mocked Facebook SDK and your Database service using setter or constructor arguments.

your tests will now do what the controllers (and maybe views) will do and verify the proper routines are called in the SDK's and that the resources are properly obtained and disposed of.

Peter Tillemans
+2  A: 

One way to solve this problem is to create two constructors in User: the default one instantiates the real-life data sources, the other one gets them as parameters. This way your controller can use the default constructor, while your tests use the parameterized one to pass in mock data sources.

Since you haven't specified your language, I show you an example in Java, hopefully this helps get the idea:

class User {
  private DataBase database;
  private WebService webService;

  // default constructor
  public User() {
    database = new OracleDataBase();
    webService = new FacebookWebService();
  }

  // constructor for unit testing
  public User(DataBase database, WebService webService) {
    this.database = database;
    this.webService = webService;
  }
}
Péter Török
Yes that's what I've been thinking about too. Simple but working. Have you ever done this before, by the way? It's like redesigning a class so that we can inject detailed implementation (which can be mocked) from outside.
Andree
@Andree, yes, I use it regularly. It is very useful for unit testing legacy code. Note that this is not necessarily a final and perfect design - but once you have your code covered with unit tests, you can go on safely refactoring it further.
Péter Török
+1  A: 

This isn't really a question about mocking, but about dependencies--and trying to unit test has forced the issue. It sounds like currently you create your User object within your Controller.

If the User and Controller have the same lifetime (they're created at the same time), then you can pass the User into the Controller's constructor, which is where you can make the substitution.

If there is a User object per call, then perhaps the User object should be passed in by the environment, or returned from some Context object.

Steve Freeman
A: 
01