Fast Rails Tests, Smoother Development

If you’re a rails developer or had an experience of writing a rails application you know that running your tests is a really painful and long process because it loads the rails environment at first. I’m assuming you write unit-tests for your rails application because it’s really offensive not to write tests in kind of an application that creates a test/spec folder for you from the very first beginning of its life. It assumes that you write tests for your app because you care about your code and software. Once again I remind you Uncle Bob’s famous quote about writing tests and TDD: “you can’t call yourself a professional developer if you’re not doing TDD.” Anyway, running tests in rails is really painful and too slow. When you are doing your Red-Green-Refactor cycle whenever you run your tests you have to wait several seconds until you get a feedback from your tests. This is not the way how it should be done. This latency and delay is breaking the cycle and is really overwhelming. Pretty soon you won’t run your tests as frequent as you should, therefore you won’t be able to get quick feedback about what you’ve done and you miss a lot of TDD and unit-testing benefits in general.

I personally write my code with TDD approach and the quick feedback is really critical during my development cycle. So this kind of slow test won’t let me to be as productive as I should. Whenever I run my tests I am like: “Son of a Gun, why is this happening?!”

Recently I watched Corey Haines talk at Arrrrr Camp about fast-rails-tests. I can’t emphasize on watching this talk enough if you care about your craft and productivity as a developer. Note that I didn’t say rails developer because there are lots of good ideas that you can take from that talk as a developer and software craftsman in general. In that talk Corey mentions about separating your domain and business logic modules in a way that don’t depend on rails. Not surprisingly, isolation and separation of concerns is a golden solution here, AGAIN. According to Corey’s example imagine you have a ShoppingCart model which has many products in it (ActiveRecord model of course) and at some point you need to calculate the total price of the products in your ShoppingCart. By default when people face this kind of problem they write a method in the ShoppingCart model which calculates the total price of the products like this:

def total_price
    products.map(&:price).inject(0, &:+)
end

BTW this is one of the reasons we will end up with “Fat Models”. Let’s ask a question from ourselves at this point: “does this really have anything to do with rails and ActiveRecord?” No, it’s obvious. So, why we put it in the model? Why we made this logic depend on the ActiveRecord and rails in general? We shouldn’t do this. This is a calculation and logic and it should be separated. There are different ways for doing this according to Corey’s talk and I definitely recommend you to watch it and learn about them because in each situation you should pick the best solution. For instance, we can separate this logic and include it in the model so it has this functionality, but the functionality implementation itself is in the right place and separated rather than being mixed with the model. Here’s the test and code for it:

it “returns sum of price for multiple items” do
    TotalPrice.of(stub(:price => 10), stub(:price => 20)).should == 30
end

module TotalPrice
    def of(items)
        items.map(&:price).inject(0, &:+)
    end
end

Then we can use it like the following:

class ShoppingCart < ActiveRecord::Base
    has_many :products

    def total_price
        TotalPrice.of(products)
    end
end

Now you can have your tests for the total price calculation separately from the model and it has no dependency to rails which is the right thing to do. Your tests for this logic will run extremely fast with no latency and delay because of good separation of concerns. I love this idea and ever since I watched this talk by Corey I’m looking for potential Refactorings in my rails apps for separating my domain and business logics and calculations from my models. One of the perfect candidates for this was in my User model. I have some password machinery in my User model which encrypts the password before the user is saved in the database using the before_save ActiveRecord call back. It kind of looks like this: (I made it so much simpler for focusing on the purpose of this post):

class User < ActiveRecord::Base
    attr_accessible :password
    before_save :encrypt_password

    def encrypt_passowrd
        #some password encryption machinery here
    end
end

My tests for this encryption are really painful like all the rails dependent tests because they take too long to run. But then it hit me that this machinery has nothing to do with rails, it’s an encryption algorithm that I use for my password security issues. So I can SEPARATE this logic from the model perfectly. So I ended up writing the following module:

module PasswordEncryption
    def encrypt(str)
        #some encryption machinery here
    end
end

Then I changed my model accordingly:

class User < ActiveRecord::Base
    include PasswordEncryption

    attr_accessible :password
    before_save :encrypt_password

    def encrypt_password
        encrypt password
    end
end

Now I can run my tests during my development cycle very quickly and get fast feedback which is one of the main purposes of TDD and unit-testing in general. My development process for the business logic and calculations part of the application which BTW are most important parts, became so much smoother and faster and I can completely see the improvement in my productivity when I’m working on a rails app. Here are screenshots from running password encryption related tests in my app before and after this Refactoring and design change:

Look how much difference that design change made. I loved this point by Corey during his talk: “most of the times when people have troubles testing something in their code either about running time or testing the functionality itself they change their tests but it’s Test-DRIVEN-Development and it means that we should change our design because that difficulty in testing shouts about design problem which needs to be solved. So, we should change our design NOT our tests.”

I found this really simple trick which helps me a lot while I’m hunting for this kind of Refactoring and design candidates in my rails apps, whenever you face something in your code ask yourself this simple question: “does this have anything to do with rails?” when the answer is “No” go ahead and separate that logic. Try this and you’ll be surprised and thrilled about the fact that you will develop your app so much SMOOTHER and more FLUENT!

Hope that helps, it helped me a lot.

 

Related Posts:

Running Rails Rspec Tests – Without Rails

Making ActiveRecord Models Thin

Architecture The Lost Years (Uncle Bob)

Advertisements

4 thoughts on “Fast Rails Tests, Smoother Development

  1. In your TotalPrice object, why does it have to care about products? In your example, you pass it products, which are a collection of ActiveRecord objects. Why not just pass it the the prices and let the TotalPrice object do the summing from the Float/Fixnum? Then, you don’t have to worry about an object being injected that responds to #price.

    For instance, what if there was another model that stored a column and it’s name was #total_price, or #cost_per_unit, or some other variation. Better yet, what if we wanted to get a TotalCost from somewhere other than our database? (CSV, XML, etc) Ask yourself: Do we care how the persistence layer names the column?

    The TotalPrice object wouldn’t be a drop in replacement – you would have to alias the method to ensure it responds to #price. However, all you care about is taking a set of Floats/Fixnums, and summing them up – they shouldn’t care about the method name that your ActiveRecord model references them.

    Your test also shows this, as you have to call stub(:price => {amt}) multiple times in the constructor. If you didn’t care about a method definition (#price), and just accepted an array of Float/Fixnums, then you wouldn’t have to stub anything out – it’s a collection of amounts.

    Your tests still benefit from the speed, and your code benefits from the decoupling and single responsibility.

    • Thanks for your great points Nate. The point about SRP and also decoupling from some irrelevant logic is great. You mentioned about what if we’ll have #total_price or #cost_per_unit? then we won’t be able to reuse this module because of the dependency to the :price method. I agree with you about this issue but according to YAGNI at this moment there’s no such things and we only have this product object with price method in it which we care about. If in the future there would be something else which can use this module for total price amount the design should definitely be changed accordingly and take the amount collection and don’t care about whether it’s price, total_price, cost, etc. then it’ll be completely ready for upcoming changes in the future in this category.

      This is Agile/XP I guess which we don’t do over-engineer and we don’t look for what future will bring in this way cause maybe future never bring that thing. We do the thing that work just fine for this specific scenario but when a change comes we’ll handle it in a way that all the further changes in that category will be handled automatically. If I’m missing something here, I appreciate your response.

      Good Luck 😉

      • Thanks, Sam. I enjoy the discussion.

        If you were *truly* adhering to YAGNI here, then you wouldn’t need the separate object at all – you could just do it all in the model (after all, you may not ever need to use that calculator code again). Be careful with YAGNI.

        With this mindset, you take the simple approach of ‘get it out of the model’ – yet you still feed it ActiveRecord objects. This is the same approach that Corey described where we used to shove the code from the controller to the model. The goal of the calculator shouldn’t need to adhere to #price or any other method – it’s just summing up Floats.

        You are still thinking with your Rails hat on. Step back, look at what you need: You have a collection of prices, and you need to sum them up. If it wasn’t for Rails and your Product model that had a #price attribute, would you make the same decision to inject a collection of products?

        I consider YAGNI to be different than the case described here. You *do* need the code to do the sums – the calculator. YAGNI is not a free card to write less than optimal code for the sake of Agile. The goal is to write better (cleaner, better maintainable, SRP) code.

  2. Thanks Nate.

    Actually there’s a little misunderstanding here. I completely agree with you that maybe I won’t need the calculator logic never again. But the point is that I extract it out for Single Responsibility Principle and also the reason of extracting it out was increasing the speed of my tests which achieved perfectly here. I applied YAGNI here form the following point of view: At this moment I don’t have any other thing that I want to reuse calculator for it, so it only needs to work with price of products so I didn’t make it more general but at this very moment I needed to speed up my tests cause my tests were slow and that shouts about a design problem which I mixed my logic with ActiveRecord model which is not necessary. So I separated it and extracted it out according to this current need for increasing the speed of tests. I adhere to YAGNI from this point of view because I needed to speed up my tests at that moment but I didn’t have any other thing which needs using calculator logic. But form the direction that you’ve mentioned I agree with you.

    And the purpose of this blog was about having fast rails tests so I wrote enough code for achieving that goal. But of course the next Refactoring step in this code can be making the TotalPrice module independent from product, price, etc. which will be a great Refactoring. As you can see I didn’t do this in the next example (PasswordEncryption module) in that case I pass the encrypt method a string. It doesn’t matter whether it’s a password, username, surname, etc. it gets a string and then encrypt it, completely separated from Model and its attributes. So in the TotalPrice case this can be a good Refactoring candidate too.

    Thanks for your point, I appreciate it 😉

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s