A mighty factory worker

In my last post i ranted slightly about having a factory instead of fixtures and why i think it's worth doing. The problem with Dan Marges' Factory is, however that it tends to create a whole lot of junk you don't need.

Allow me to explain. Let's take this snippet

def self.create_newspaper(attributes = {})
  default_attributes = {
    :customer => create_customer,
    :headline => "Read all about it!"
    :paperboy => create_paperboy
  }
  Newspaper.create! default_attributes.merge(attributes)
end
Let's say we had a paperboy already created in this particular situation and we passed it to the method via the attributes param. After this call, like it or not, we'll be stuck with two paperboys. Add to this validations and the fact that the point of factory is to write less test data than you would with fixtures and you'll most likely see my point.

So let's recap what we'd like. We'd like something that creates all attached models to the one we call, and we'd like it to do so without screwing up every single thing in the universe. Sounds easy enough.

Let's say we have pickle which belongs to jar which belongs to shelf. When we call create_shelf we'd like our mighty class to also create pickles and jars to go along with our nice green shelf.

Now at the time i had realised the simplicity of my needs, i had already gone into a great deal of trouble and writing to order my functions in a 'factory' sort of way. I had create_pickle, create_jar and create_shelf all ready, and waiting for my signal. Not wanting to reformat all my code, i namely my newly-created method 'clever_create'. Its story was as follows

  def self.clever_create(klass, existing = {})
    call_stack = [:pickle, :jar, :shelf]

    create_params = {}

    call_stack.each do |current|
      create_params[current] = existing[current] || eval("self.create_#{current}(create_params)")
      break if current == klass
    end

    create_params
  end
That should pretty much take care of creating your objects for you and return your whole bunch of stuff in one neatly packed hash. The only problem is that, since you're doing a merge on the create_params in your create functions, some of those keys might not have methods in your models. As such, you should change your merge to an exclusive merge, which, oh noes, is not present in the manual ! What are we to do ? One option would be to run around screaming in anguish and dismay until we feint, another would be, as we are in ruby, to just create one.
class Hash
  def exclusive_merge(other)
    self.each_key do |key|
      self[key] = other[key] if other.has_key?(key)
    end
  end
end
So there it is. If there is one already, i couldn't find it because it isn't called 'exclusive_merge'. If there isn't, then it's all good.

There would be more to add, but sadly my alcoholic beverage of choice is getting quite warm by now, and i must continue another time. As such, i bid you farewell and good fortune !

On testing - part one of many

Hey there, people. As you may or may not have noticed, i haven't really been writing all that much for the past few weeks, but now i have returned and am fully prepared to deliver upon the afore-promised bacon !


So i've been reading and writing a lot of tests for an existing application, and the grand conclusion i have come to is that fixtures are rubbish. There are a number of tests one can not carry out without using fixtures, but for the greater part using a Factory-based approach is more fun to both read and write.


The reason i like creating objects in my test rather than having fixtures is that it's easier to keep track of the things i have in my database and it makes the models easier to understand for people who are new to the codebase. For you it may be quite obvious that you need to create a Widget before you create a Kludgel and a Porkchop that need to be linked to a Fricasee and a Frisbee with a diameter greater than 15, but having to swim around the codebase to gather that whole structure would take a lot more time and be considerably more boring than just having it laid out in front of you in a test and being able to play around with.


One thing that doesn't really work that well when you're writing tests for an existing codebase is mocking. You generally want to write code with mocking and ease of testing in mind. What that usually leaves you with is a lot of skinny methods just ripe for mocking, stubbing, and isolating the liver out of your unit tests.


Also, another thing to look out for is that posts containing no code at all look surprisingly smaller than posts containing code. As such, I shall return soon bearing gifts of both code and posts !