Puppet: System Administration Automated

Testing Initialization Code


One of the things I continually struggle with in testing is code that runs during initialization. A lot of times this code is very simple:

def initialize(name, cert, key)
    raise Puppet::Error, "Cannot manage the CRL when :cacrl is set to false" if [false, "false"].include?(Puppet[:cacrl])

    @name = name

    unless read(Puppet[:cacrl])
        generate(cert, key)
        save(key)
    end
end

It's easy to test that first and second line, and it's entirely obvious what that last bit does, but it's fantastically difficult to test, especially if you follow the advice of Jay Fields and try to stick to one expectation per test.

If you do it all in one test, you end up with a relatively long test that covers a single specific version, but it doesn't describe the behaviours very well. You want something like this:

describe "when initializing" do
    it "should fail if :cacrl is set to false"

    it "should set the name"

    it "should read the crl in from disk"

    describe "and no crl exists on disk" do
        it "should generate a new crl"

        it "should save the new crl"
    end
end

The only way to do this, though, is to use stub_everything, and then individually test for each method, which is messy.

Even worse, you now have to stub out these methods every time you want to test an instance of the class in any other way. For instance, (as you might have guessed) I'm remodeling our Certificate Revocation List as a class, and I'm going to need to test the actual revocation, along with storage to disk. Each of these are made more complicated by the code in the initialize method.

Why, then, don't I just leave the code out?

Well, I could easily have it lazy evaluate, only running when someone actually asks for the crl. The problem is that I've consistently found that lazy evaluation causes more problems than it saves. I tend to run into permission problems (because the code doesn't evaluate until puppetmasterd is running as puppet, when it sometimes doesn't have the permissions it needs), and it's just very difficult to really control ordering.

Also, it just feels messy to reorganize easy code to make it more testable. There seems to be a postulate in the testing world that code that's difficult to test is bad code, but I defy anyone to argue that the above code is unclear or "bad code", other than just directly saying it's bad because it's hard to test.

I expect that in this case I'll have a generate_and_save method that, well, generates and saves, or maybe a load_or_create method that does this bit. Yay. Because simple code is hard to test, I end up with less simple code, and I still have to use stub_everything.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Tue, 01 Apr 2008 | Tags: , , ,


Name:


E-mail:


URL:


Comment: