Posted: Jun 1, 2012
in general

By Felipe Iketani

Use Dependency Injection, Your Tests will Thank You


Doing Outside In development is important. You will be able to better design your classes when dealing with use cases or stories.

Imagine you have a notebook, when you turn it on, you also need to turn on its monitor and keyboard.

Let’s write some acceptance tests for this.

Let’s try to implement the acceptance spec starting with Notebook:

Hum, of course it will fail for many reasons. First, we need Monitor and Keyboard classes and they need to respond to #turn_on. We need two already implemented classes just to start writing our Notebook class!

Dependency Injection to the rescue!

Dependency Injection is very discussed in programming development for decoupling code. In Ruby we feel like we get it for free. It improves our code design by decoupling classes and eases our tests.

So, let’s start writing unit tests for Notebook with Dependency injection.

Nice! We didn’t need Monitor and Keyboard classes to write our Notebook code! We can test everything in isolation because we’re able to stub our methods/constructors inputs, so we won’t need the actual objects.

Remember, Ruby is a very powerful dynamic language. It doesn’t care about type, only about their interfaces. If a Monitor and an ExpensiveMonitor behave like the same for the computer needs, it will work.

Our unit tests for Notebook are green now! Yay! Look, we don’t use Monitor or Keyboard objects, we use stubs, so you need to pay attention to some points. Look at the #as_null_object I added in monitor and keyboard stubs. It’s needed because our stubs don’t respond to #turn_on.

Each expectation mocks the #turn_on in a SINGLE object and expects it. But the other doesn’t respond to it, so each test would fail because of the other dependency.

Using #as_null_object, we tell the stub to respond to any method in any test, so it won’t break other tests so each test will have only one expectation.

So our Notebook works as expected, but our acceptance test is still red because we don’t have Monitor and Keyboard.

And our monitor and keyboard:

Now our unit and acceptance tests are green. We did Outside In development and improved our unit tests with Dependency Injection. We didn’t need Monitor and Keyboard at first to test our Notebook.

This is a silly example but in the real world our classes have many dependencies and if we begin writing things the Inside Out way, we will write more code than we need.

In 2009, Joseph Wilk had a very nice talk about Outside In development With Rspec and Cucumber on Scotland on Rails 2009.

You can start Outside In development from acceptance UI tests until the core of your application’s unit tests so you won’t loose yourself writing code you don’t need, and Dependency Injection will help you develop your project step by step, making you happier and your life easier.

Webbynode is Web Application Hosting for Developers Lean more .

Leave your thoughts

  • http://twitter.com/macarthy Justin MacCarthy

    You should read Objects on Rails by Avdi Grimm 

  • nmk

    Lecturing on DI and writing abominations like

    @power == true ? true : false

    don’t mix well.

  • http://teamcoding.com/ Kris Leech

    Nice example and thoughts. To add to @nmk `@power == true ? true : false` should be `!!@power`, the double !! will ensure false is returned if `@power` is nil.

  • Felipe Iketani

    thanks, this was new to me :)

  • Felipe Iketani

    yeah, really ugly, it was the kind of stuff that i forgot to refactor before publishing.. but you’re right!

    thanks for the feedback

  • Felipe Iketani

    Objects on Rails is very good indeed. I also suggest every one to read it!

    The purpose of this post is not about using DI and Outside in development in Rails, but in any kind of project/test. 

    Good approaches are framework agnostics.

  • Kreimer Georg

    Why not just?

    def on?    @power  end

  • Felipe Iketani

    personal taste :)

    @power  == true can return only true of false.

    @power just can return anything.. i just follow this convention

    this is a really small class, so @power == true may seem silly. but i think it’s still readable and understandable. 

    just a personal taste

  • Kreimer Georg

    Agree, once @power would be set more complex it makes sense :)

  • http://itreallymatters.tumblr.com Jarmo Pertman

    And using RSpec you can do like this:
    subject.should be_on

    instead of
    subject.on?.should be_true

  • Ticktricktrack

    Nice one

  • http://architecturalatrocities.com/ Theo Hultberg

    Not sure if it can be considered idiomatic, but it’s common to write `!!@power` if you want to make sure you’re returning a boolean. The single negation will make sure it’s a boolean, and the double makes sure it’s not negated.

  • Felipe Iketani

    The issue about !!@power or only @power is that if I set @power = ‘off’, both of then would fail for example.
    in my opinion, any method finishing with ‘?’ should be careful. that’s why i enjoy @power == true

  • http://architecturalatrocities.com/ Theo Hultberg

    Um, if you set @power to “off” I assume you would also use “on” for the opposite case, and that means it would fail too.

blog comments powered by Disqus