There is no good or bad, just fun and not fun

Life, code, stuff, things.
Last updated 9 weeks ago says livejournal. Well done me.. No, wait - the other one.

So my personal life has been fairly crazy recently, everything is starting to calm down and return to normality, but I appear to have got a social life back, so I don't have as much time for coding etc as I have in the last 18 months. This probably isn't a bad thing as I was going slightly (more) insane, in a bad way.

However, in the last 9 weeks we have managed several releases of Catalyst, the new -Devel stuff is getting nearer every day and I finally finished the stuff that I've been talking about with MooseX::MethodAttributes for aaaaages.

I have to admit that I probably wouldn't have got there yet if it wasn't for the extreme awesomeness of rafl, who did all the core Moose refactoring needed to support this. He went on from that to release CatalystX::SimpleLogin for me, which works well enough for simple use.

So, rather than just a meta-post about stuff which has happened already, here is some code for you. This is a quick tutorial on how you might test a Catalyst component by mocking things, and also demonstrates the hard to grasp concept of 'currying' in a fairly easy to understand fashion..

So, given that you've got that open in another tab, let me try to explain it (and why it is the way it is) for you a little. So the basic premise for this is that you want to write a Catalyst component, and you want to write unit tests for it. And, well, they're NOT unit tests unless the entire environment past the code under test is faked.

I know that most Catalyst applications and components have 'live' tests, and they're great - you should be spinning up a real app with your component included to test that it works in-situ. But trying to test all the obscure combinations of stuff you may have to deal with by generating HTTP requests and reading the responses back - that's a massive pile of pain and suck..

So mocking a $ctx is pretty easy, and you can then just say: my $comp = My::Comp::Class->COMPONENT($c, { %config }); and off you go.

Except that building a $ctx takes a bit of effort - no bother, you can shove that in a library.. Except you're still going to be writing loads of boiler plate to stuff all the necessary data into your mock request etc for each test case.

So, the obvious solution here is OO - start being able to clone your mock ctx instances and customise them, etc.. And you quickly end up with OO modules to run assertions on your $ctx, and - aghhh. You've just written your component a second time - backwards. And it's got all the same bugs and all the same assumptions, as it's within the exact same programming paradigm.

So instead of that, lets have a 'ctx generator' function, which you can call with some data and it'll setup the ctx. Being of simple mind, I decided there were only two things I needed: 1 - to be able to fill the stash with stuff (as I'm probably gonna do that all the time), 2 - to be able to mangle the generated context.

So going to my code sample:

  • Lines 14-29 create a class to be the mock context

  • Lines 31-38 test this works as expected

  • Lines 40-52 make an anonymous function which when called generates a context and populates the request/response

  • Note that you can pass this 'generator' a callback function which is called on the context after it's generated. This has no use _right now_ (as you could just call stuff on the ctx you're about to get), but will be useful later

  • Lines 54-65 test this

  • Lines 66-74 are the 'clever' bit here - given a ctx generator function, it customises it (by the first parameter, also a function) and returns a ctx generator function.

  • Line 77-93 then test this

  • Line 103-112 shows the same approach for bundling tests

  • Line 125 then is the finale, running a combined set of tests against a context which is is generated and permuted several times

So, I'm assuming (hoping) that everyone follows what's going on after peering at the code a little.

The nice thing about this is that you can successively build up more specialised ctx generators just by taking the generator you have and customising it some more.. And then you can just generate a customised ctx.

The really nice thing about this is that you can wrap bundles of tests up and pass them the context. So you can build up sets of tests and sets of test scenarios (i.e. mocked contexts with all the 'basics' filled in, just the 'for this test' specifics overridden) incrementally, without ever repeating yourself / having to copy around or repeat the same mock data or mock test cases.

Anyway, explanation over - I hope that demonstrates the technique sufficiently. I hope this has made you think about the subject, or given you a different perspective on how you could tackle the problem, even if you hate the approach shown here.

Thanks for listening, I'll try to bore you again in under nine weeks time :_)


Log in

No account? Create an account