Object factories vs. merging new values to fields in JS
We had interesting discussion about one of commits, which changed way for delivering stubbed server responses in our test suite. In short: we have jasmine-based acceptance tests for client-side, which uses jQuery .click and other event triggers to simulate interaction with user. All requests to server-side are stubbed, so we have to produce some JSON-like data structures to respond with them. Almost every test use the same data, but sometimes we need to change something to test specific scenario.
1st solution
It was based on global object ServerResponses with few fields for each type of request. That fields contained objects, which were stub responses for specific request. Take a look at the code (CoffeeScript):
OK, not bad, but what if I want to change something in player attributes, for examples his points?
Now you see where the problem is – if we want to change inner object we have to clone it. It sucks.
2nd solution
Here’s the point, where the discussion begins – I replaced initialData object with initialData object factory. In CoffeeScript the change is syntactically small:
So as you see each response is represented by new object, instead of shared one. We can pollute these responses with everything we need, we don’t have to think about global state. But there is one problem – it’s a function, so we have to remember about “()”. That’s sad, awful and too complicated.
Is there any chance to get this as beautiful as furry bunnies on green grass?
3rd solution
So let’s design perfect solution for this problem. Let’s have a function “merge”, which would create new object from object given as first parameter, and new values in second one. “Perfect code”:
It looks nice. If we assume, that every object can only update its fields or extend with additional fields – it seems doable. We can live with this assumption, but let’s think how it can be implemented… First of all – for each field of second parameter object we must create clone of corresponing object in first parameter. It has to be deep clone of object sometimes, so it isn’t easy to implement. Please, remember the context – we want it as tests helper. So to get “perfect code” we would have to implement helper, which also needs to be tested. But ok, we can do it in 1 hour – because we love “perfect code”.
What’s the biggest problem with that solution? That it doesn’t protect us from affecting global state of tests. We still can do ServerResponses.initialData.player.points = 10.
Summary
Object factory:
- has ugly “()” in every use (eeeew, worst thing ever)
- always use new object, without reference to global state
- has ugly name – factory
- has simple implementation
Merge function:
- syntactically beautiful
- has complex implementation (we are awesome, so we’ll write it with full test suite)
- doesn’t protect global state