Tuesday, February 21, 2012

Not a Book Review: Hunger Games

I just finished reading The Hunger Games by Suzanne Collins (at the recommendation of i3ears). This book filled me with that rare combination of delight and rage. Let's start with the delight.

N.B. This post is has some spoilers, so I'm putting the rest of this post below the fold. You have been warned.

Monday, February 13, 2012

How to Fix Call of Duty, Part 1: What Can They Do

I have been a Call of Duty (CoD) fan since Call of Duty 4: Modern Warfare (CoD4), the game that somewhat revolutionized the way first-person shooters were played (things like killstreaks, perks, weapon attachments). I've dutifully bought every CoD game on release day since then. I even preordered MW3's "Hardened" edition (and I hate preordering games). So I can rightfully say that I'm a CoD fan.

The gaming community is pretty torn about CoD. Hardcore gamers swear that CoD caters to noobs too much. CoD gamers say that competing titles are too slow or complicated... or they don't say anything at all, as CoD is pretty much the FPS king at the moment. But there are always people arguing about CoD; it's a very polarizing franchise. CoD can even be polarizing to a single person; I am torn about MW3 and the future of the franchise.

CoD's publisher, Activision, and primary developers, Infinity Ward (IW) and Treyarch, have continued to innovate on with each new CoD game. World at War added Zombies mode and co-op play; Modern Warfare 2 added customizable killstreaks, pro perks, and spec ops; Black Ops added CoD points, wager matches, theater mode, and improved scoreboards. Each game also came with new or updated weapons and game modes. None of these updates substantially changed the game in the same way CoD4 did, but this incremental innovation* was welcomed by most gamers. The latest edition of CoD, Modern Warfare 3 (MW3), also brought some innovation in the form of new game modes, "pointstreaks" improvements ,and Survival mode.

* A primer on incremental innovation vs. radical innovation, from the excellent book Making Innovation Work by Davila, Epstein, and Shelton:
Incremental innovation leads to small improvements to existing products and business processes. It can be thought of as an exercise in problem-solving where the goal is clear but how to get there needs to be solved. At the opposite end, radical innovation results in new products or services delivered in entirely new ways. It can be thought of as an exercise in exploration where there might be something relevant in a particular direction but what will be found is unknown. 
However, what really has bothered many of us CoD gamers is that each new CoD title brought with it a substantial set of negatives along with the innovations. World at War had severe balancing issues, MW2 had hackers and its own balance problems, Black Ops had terrible lag/network and balance issues, and now MW3 suffers from challenging spawns, lag/network issues, and, again, balance issues. Why can't each version of the game be better and stronger than the last? Why does each title have to take two steps forward and two steps back at the same time? It's gotten to the point where people still play older versions of CoD because they can't stand the newer versions*. In fact, Black Ops, MW2, and CoD4 all appear regularly on the top 20 weekly Xbox Live titles list.

* Check out i3ears' "BEAR to COMMANDER" series for proof (and some laughs too). 

But that last point underscores why Activision, IW, and Treyarch don't care to fix these problems: Call of Duty sells like wildfire. MW3 sold 9 million copies in it's first MONTH of availability, and that doesn't even count the additional revenue they will get with DLC*. Why should they spend any time fixing problems when they can just work on the next CoD title or DLC pack.

* DLC is a blog post for another day, my friends.

My problem with this way of thinking is that Activision/IW/Treyarch can have their cake and eat it too if they are willing to make a longer-term investment in the game and community. I'm thinking of the model known as "bungee balancing" (think bungee cords; not at all related with the developer Bungie). Let's say, oh, I don't know, akimbo FMG9's are overpowered in MW3*. To find the "optimum" configuration, IW should make a drastic change that dramatically weakens the akimbo FMG9's and then monitor the overall weapon performance. Are they too weak now? Strengthen them. In this way, IW can zero in on the optimum configuration. (If you're familiar with algorithms, you should notice that bungee balancing is like a binary search.) IW could bungee balance spawn logic as well (using heatmaps as the data).

* They should be able to easily see what guns are overpowered by comparing relative weapon performance. Say players in general are at 0.9K/D player in general but a 2.0K/D player with akimbo FMG9's. That would be a clear example that akimbo FMG9's are overpowered. 

Now, I get that the lag/network issues can't just be easily worked out in this fashion, as they are much harder to test and verify. I have no easy answer for these issues, other than to say that Activision/IW/Treyarch should spend the time and money necessary to fix these issues in the next CoD title, as lag issues tend to drive gamers nuts.

We still have the question of why Activition/IW/Treyarch should care*. This sort of work isn't cheap. IW would have to have a powerful set of statistics collections and tools to manage this data. It requires a lot of effort from Robert Bowling (IW's creative strategist) and his team to identify problems and propose and implement solutions. Every update would require a full testing cycle (and title updates would require certification as well). We're talking about a lot of changes and a lot of money. Why would Activision want to seemingly waste money on this sort of work while they sell hojillions of copies?

* I don't give any credence to the conspiracy theories that Activision ensures that every CoD title has unfixed issues in order to convince gamers to buy the next CoD title. 

The answer is the long term. Sure, MW3 (essentially, CoD8) broke all previous sales records for a game, so it looks like the series is going nowhere but up. However, if you look past the sales figures, you see a lot of "CoD Fatigue" out there. Long-time CoD fans, such as myself, are starting to tire of the series. Now, for the next 10 months, Activision doesn't care about me. I bought MW3 and CoD:Elite, so they already have all the money they can get from me for MW3. But is Activision really only looking forward one year? That's not a model for long-term sustainability if you ask me.

Saturday, January 7, 2012

Something Something 2012

I once made a statement that I didn't believe in New Years' Resolutions. I still don't; why pick an arbitrary point on the space-time continuum to make new rules about your life? But I can't deny that the start of a new year does provide a semantically convenient context to take stock of one's life. And so I decided to make some new rules for myself (not resolutions!) to follow in 2012.

But, I heard on NPR (I can't seem to find the link to the story, sorry) an interview with a psychologist on the topic of New Years' Resolutions. One of the main takeaways was that telling people your resolutions actually reduces your chances on completing them. This idea is counter-intuitive to me; I always felt that by telling people my goals, I was adding their expectations of me to my internal motivation*. But the interviewee stated that by telling people your goals, you actually alleviate some of the need you have to actually complete the goal; you've already shown that you're a good person with goals.

* Of course, if you are a reader of Joel Spolsky's work, you'll be aware of his ideas (not sure if they are originally his, but I learned them from him) on internal vs. external motivation. He posits that you can't really "add" external motivation to internal motivation. The external replaces the internal. So I was doing it wrong, anyway.

I made three new "rules" for myself for 2012, but I can't tell you what they are. But I will give you a hint.

7. 3. 30. *

That's all you'll get. Enjoy 2012!

* The numbers, Mason! What do they mean?!

Tuesday, October 11, 2011

Act Now to Offend Your Customers!

Another company calling to upsell me! Does this actually work on people?

We get the Sunday paper for the coupons, and I get a call from a hurried rep. This particular rep apparently works on commission for Boston Globe, because she's trying to upsell me to the weekend edition (not even sure what that really is, she was talking too fast for me to understand). I politely declined once, and she persisted, saying she "wasn't looking for a long term commitment." I politely declined again, and she said "OK," and abruptly hung up.

My opinion of the Boston Globe has declined at this time. What could Boston Globe have done better? What if a rep (not under time pressure) called me to ask how I was enjoying the Boston Globe? What parts of the Sunday paper do I enjoy the most? What could they do to make my experience better? She would have learned that I order the Sunday paper just for the coupons. Maybe she could have talked to me as an adult and suggested ways for me to enjoy the paper I already get more than I do now. Maybe that would have led to me reading the paper. Maybe I would have decided that I wanted more issues of the Globe.

Instead, a pushy sales rep tried to push an upsell on me. Bush league, Boston Globe.

Thursday, October 6, 2011

This Is How We Do It; or, Customer Service Done Right and Wrong

Right on the heels of my last bad customer service experience, I've got two more tales about how to do it right and wrong.

I've been locked out of my Sovereign and Fidelity accounts for a while (I tend to forget my passwords) and finally decided to call them up to restore access. 

I called Sovereign first. Calling the number in their "Your account has been locked out" message, I find that the number is their generic customer service number. Great, now I have to navigate the phone tree to find the "I'm an idiot and forgot my password" option. I finally get through to a rep. He asks me a few security questions in a near-monotone voice and unlocks my account. Without asking if I was able to log in correctly, he asks if I need any more help. I answer, "I'm all set," and he launches into an upsell attempt for either identity or overdraft protection. I cut him off once with "No thank you." but he keeps going. Realizing he is going to be graded on whether or not he makes this pitch, I wait until he's done, then restate, "No thank you."

With Sovereign fixed, I call Fidelity. Phone tree asks my SSN, and then immediately connects me to a rep. This rep is much more upbeat, even making jokes with me (well, laughing at my pitiful jokes). She is able to unlock my account and offers to wait while I log in. She asks if I she can help me at all, and I ask for clarification on a prior issue. She answers my question, and that conversation leads to a discussion about me possibly opening up a Fidelity IRA in the future. 

I noticed several key customer experience differences between these calls:
  • The Sovereign rep sounded bored out of his mind. The Fidelity rep sounded excited to talk to me.
  • The Sovereign rep clearly had a script to follow. The Fidelity rep was given more flexibility.
  • At the end of the Sovereign call, I started thinking again about moving to a credit union. At the end of the Fidelity call, I felt excited about possibly opening a new account with Fidelity.
  • When the Fidelity phone system took my SSN, it knew that I needed to speak to a rep, so it directly routed me to one. Sovereign's phone system made me jump through hoops to get to the right rep.
But if you look at the technical differences, there are really only two: a smarter phone system and a better customer rep. This can't be that hard to set up, can it?

One More Thing...

I'm really surprised how much Steve Jobs' passing is affecting me. I always thought he was a brilliant innovator but an arrogant asshole as well. I poke fun at Apple fanboys, yell about Apple anti-competitive behaviors, point out ridiculous pro-Apple bias.

And yet, you can't even begin to imagine how far Steve brought the tech world forward. Without question, his singular leadership made our world (not just tech, but the way we live our lives) better.

RIP, Steve Jobs.

(who is cutting onions in here?)

Tuesday, October 4, 2011

A Wasted Opportunity

Just got a call from someone representing Dell (may have been outsourced) who tried to up-sell me on a more expensive warranty than the one I purchased with the laptop I bought a few months ago. The rep was extremely pushy, despite my gentle and polite but firm insistence that I did not want what he was selling. I finally had to hang up on him (I suppose I could have lied and told him I no longer owned the computer, but I wanted to see how the truth would work).

Seriously, Dell? I try to defend you, but then you pull a terrible customer service move like this. You had an opportunity to have a human conversation with me about how the laptop was working out, seeing if there was anything I needed help with. Instead, you insult me as a customer.

When my father bought an iPad, he received a custom email a month later from the sales rep asking how it was going and if he could help. Nothing about money or sales, just making sure he was settled and happy. The rep then helped my father fix a problem he was having without referring to outsourced tech support.

See the difference? Forget about the relative performance of Macs and PCs. The meta-ownership experience of the two systems are light-years apart. This is something that seems so easy to get right, and yet PC manufacturers (and plenty of other companies... ask me about my GM experience sometime) continually get it wrong.

Friday, June 17, 2011

One of the Little Things That Makes Me Rage

I've been trying to learn my hotkeys for Visual Studio so I can operate much faster. The most common one I use these days is attaching the debugger to IIS. For years, in Visual Studio 2008, I would type "alt, d, p, w, enter". That would open the debug menu, select "Attach to Process...", select the first item that starts with "w" (almost always w3wp.exe, the IIS process), and attach to it.

Then came along Visual Studio 2010. I don't know what changed or why is changed, but now, I have to hit an additional "enter" in the middle: "alt, d, p, enter, w, enter". When using both VS2008 and VS2010 at the same time, this can get really freaking annoying.

Thursday, June 16, 2011

I Don't Get Prisons; or, Rehabilitating our Penal System

The topic of prisons, punishment, determent, and rehabilitation has been getting to me lately, and an NPR broadcast about Angola Prison really drove me to try to put my thoughts into words. The way I look at it, our criminal justice system looks sort of like this:

Crimes are actions that we as a society have deemed illegal because they cause harm to other people. Murder, burglary, and libel are pretty obvious examples of crimes that do harm to others. Self-inflicted crimes (most notably, drug use) have secondary negative effects on society. 

As a society, we decide that people who commit crimes should suffer some consequences with the idea of reducing the number of criminal acts. For most crimes, the sentence is prison time. As I see it, the justification for prison time serves three purposes:
  • Determent: If people know they are going to jail if they do illegal things, they will be less likely to do them.
  • Rehabilitation: Criminals serving time may become rehabilitated and not want to commit crimes again either by a change of morals/outlook or by the fear of being imprisoned again).
  • Punishment: Society demands that people who hurt them are in turn hurt (the old Babylonian "eye for an eye" morals). 
It seems pretty clear that determent doesn't work for a large portion of society. People who have been driven to commit crimes (either by extenuating circumstances or by a weak moral compass) usually seem to just try harder to not get caught. People who wouldn't commit crimes in the first place because of a strong moral compass (e.g. me) are less likely to do the illegal activities even if they were not illegal*.

* I'm well aware there is a huge amount of sociological and psychological research in the fields of motivation and morality. 

Punishment seems to make sense, except when you think about the longer sentences. According to Louisiana's data on Angola, 73% of the inmates in 2010 were serving life sentences (there is some math at play here; lifers only leave when they die, whereas other inmates can finish their term or be paroled). What's the point of locking someone up for their entire life? Seems like a waste of a life.

Rehabilitation seems like the most valuable thing we can attempt with our criminals*. My wife once worked in a batterers intervention group, where she worked with convicted domestic abusers. After a once-a-week-for-40-weeks program, the recidivism rate was far lower than those who went to jail (granted, it is likely that this data is somewhat self-selecting; the judge could sentence intervention instead of jail time, and likely chose the more repentant men for intervention). I bet you many more criminals could be rehabilitated rather than just thrown in a snake pit with other criminals.

* I fully acknowledge that some people cannot be rehabilitated, and that life in prison or capital punishment are the only reasonable options for society.


I don't know where I'm going with this, other than to say it seems clear that our penal system could really use some work. Prison inmates are a drag on society, producing nothing, consuming goods and services, while (generally) falling behind in job skills. It's the 21st century; can't we do this better?

Thursday, February 3, 2011

resharper - surround with

I recently discovered a really useful Resharper command for Visual Studio: "Surround With". Simply hit "Ctrl+Alt+j" and you get a nifty menu with a list of common things your would surround code with. This feature makes programming simple: write the code, then add the hardening later. Booya!

Tuesday, February 1, 2011

Visual Overload

I reached my record of running five concurrent instances of Visual Studio (four 2010, one 2008) recently. Seems like there should/could be a better way to have multiple solutions open at once, especially since each VS took about 500MB of memory each (with 8GB memory total, this wasn't really a problem, but it could be if I also had other high-memory programs open). Maybe one VS can have multiple solution-spaces open, like tabs, but at a higher level than the tabs for open files? Not sure, but it seems like a better answer is waiting for someone to discover it.

Sunday, January 30, 2011

On Troubleshooting

I think troubleshooting is a distinct skill that is technology independent. For example, Annie just called me in to the living room because the TV was displaying "No Signal", despite the fact that we were successfully watching TV earlier in the day. I don't really know anything about TVs and DVRs and tuners beyond "you plug the cables in where they fit", so I was slightly concerned. However, all my years of troubleshooting software bugs came through. Here was my process:
  1. Sanity check: make sure all cables are plugged into the right ports and the TV is set to the right setting. Still not working.
  2. Sanity check: turn off and on DVR and TV. Still not working.
  3. Move HDMI cable from port 1 to port 2 on TV, then port 3. Still not working.
  4. Get new HDMI cable from basement and test DVR and TV. Still not working.
  5. Test laptop HDMI into ports 1, 2, and 3 on TV. Laptop output to TV not working. 
  6. Unplug power from TV, wait 10 seconds, plug back in. Eureka! It's working!
The trick is to continually isolate a single factor and remove that factor from consideration. If I had not tried the laptop, I doubt I ever would have figured out the problem. But I needed to test with an input other than the DVR to isolate the DVR and TV from each other to determine which was the problem. I'm just glad I was able to fix the TV so easily. 

Friday, October 22, 2010

Derail

So the 30 day meditation challenge did not go so well. It turns out that if you leave the house at 7AM and don't get home until 10PM (welcome to my Mondays and Wednesdays), it's hard to find any time in a quiet place, and when I do find that time, I fall asleep. Off the bat, I lost the challenge on the first Monday, but then the next day I had too much schoolwork to do, so I had no time that day... and so on.

A more realistic goal for myself would be to meditate three times a week. That way, I can make plans to meditate on certain days, and if the plan doesn't work out, I have time to make it up. Good idea, Ed, let's do it!

The lesson I'm taking away from this is that it is unrealistic for me to attempt to force myself into a difficult arbitrary structure simply will not work. I'm too much of a "seat of my pants" kind of guy (on the MBTI scale, I'm definitely a perceiver).

Monday, August 23, 2010

Next Stop, Meditation Station!

I've been meaning to try meditation for a long time. I read a fair bit about it (starting with The Relaxation Response and moving to the Internet), and after talking with a few people, decided now would be the best time for me to try it.

So I challenged myself. 30 days, every day, meditating. Just 20 minutes (with a few minutes on each side) a day, I can do this, right?

Well, it took me about a week from when I said that to when I first meditated. But that first meditation was yesterday! So that's good. What about today?

I decided the best way to help me continue to meet this personal challenge would be to post it online and give daily updates. This would both A) get me blogging again, and B) provide me with extra incentive. After all, I can't let my (two or three) readers down, can I?

Wednesday, July 28, 2010

Nitpicking a House of Cards

I love the movie The Mummy. It's just a fun movie, and I'm not alone in liking it. The combination of horror and action and comedy (and even zombies!). But there are several bits of it that always piss me off.
  • In the beginning scenes with Ardeth and the Medjai, there are like hundreds of Medjai. But once Ardeth and Dr. Bey confront Rick, Evelyn, and Jonathan about awaking Imhotep, all the other Medjai disappear, off doing who-knows-what. It should would've been useful to have even a dozen scimitar- and rifle-wielding warriors helping out. (At least in The Mummy Returns, they mentioned the other Medjai had to get ready to fight the Army of Anubis.)
  • After Imhotep has "absorbed" three of the Americans, leaving only Mr. Daniels, the party is driving in car to get away from Imhotep's zombies. They know they need to protect Mr. Daniels to prevent Imhotep from getting fully regenerated (another case when more Medjai would have been nice). So why do they leave him unprotected at the back of the car? Dumb.
  • And then after the zombies pull Mr. Daniels off of the car, the party keeps driving on, knowing full well that they are letting Imhotep get fully regenerated. Sure, there were plenty of zombies, but Ardeth, Rick, and Dr. Bey were all pretty badass fighters. You'd think they could've held off the zombies while Mr. Daniels got back in the car.
But this whole blog post is a waste of bytes. It just bugs me every time, probably because I enjoy the movie so much. 

Wednesday, May 12, 2010

Oh, By The Way, New Baby!

Yeah, so my lack of posts recently was first a result of pregnancy (Pregnancy: not just for expectant mothers!) and now a result of our firstborn, Mia MacKenzie!



You can find more photos of our beautiful daughter at Flickr. Mia is more important than just about anything I've ever done, but I will attempt to get back to posting a little.

Thursday, February 18, 2010

What is a TypeLoadExceptionHolder and What Should I Do If I Have One?

Recently at work I was creating a new branch in Subversion for one of our projects when I ran into a little error on one an integration test called "Should_Send_Data_From_File_To_Server":
Object of type 'System.Runtime.Serialization.TypeLoadExceptionHolder' cannot be converted to type 'System.Collections.Generic.List<companyname.framework.persistableobject>'
TypeLoadExceptionHolder? What the crap is that? I tried Binging it, and after pouring through results, found this post by Michael Freidgeim:
Binary deserialization doesn't throw exceptions, but just replace some members(in my case it was enum, that was moved from one namespace to another) with this undocumented TypeLoadExceptionHolder class.
OK, so why was my binary deserialization failing? I wrote up a quick integration test to simply serialize and deserialize a file and it worked fine. But the sample serialized file just wouldn't deserialize in this branch, even though this exact code worked fine in the source branch.

I was nearing my wits end when I realized Michael had already nailed it for me: namespaces. One of the reference assemblies, "companyname.framework", had changed names in this release branch. It used to be "companyname.framework.core". And so the sample serialized file had the old namespace and couldn't properly be deserialized. The mystery is solved.

The moral of the story is Never trust canned data in your integration tests. Always suspect it when sometime goes wrong.

Friday, February 12, 2010

Force Feeding You Change

Google loves to just drop new features in your lap. I remember when I first noticed Google Talk; I came back to my computer and Graham had dropped me a line, "yo fezzik, whats shakin bro?" (of course, Eric was the second person to send me a chat). I was all "WTF is this? Get out of my inbox!" But I got over it.

It reminds me of the way Blizzard would work their betas. For the Warcraft 3 expansion beta, when they added a new unit, they would make that unit overpowered so that everyone would use it (When they added "fragmentation shards" to the mortar teams, they made the effects so loud and big that they were impossible to ignore). That way Blizzard could see how people reacted to it, how they used it, etc. They would use that data to find the right balance.

That's just like what Google has done with Talk and Buzz. They just throw it right at you, in your face, in Gmail. You have no choice but to deal with it (yeah, sure, you can disable them both, but how long did it take you to find that little link?). And so now everyone is talking about Buzz, using Buzz, and Google can use all this feedback to flesh it out properly. Nicely done, Google.

Wednesday, February 10, 2010

Google Buzz: The Facebook-ization of Google

Google surprised me when they suddenly launched their latest endeavor into the social networking world, Buzz. After one look and their introduction video, it seems clear they are targeting one competitor: Facebook. Sure, Buzz includes features from Yelp, Foursquare, Gowalla, and others, but those aren’t the main targets.

Think about it. Facebook is working on a new email feature. Facebook’s goal is to have you on their website for as much time as possible. I know plenty of people who would almost never leave Facebook if they could get their email there. Sure, there would still be users like me who actually post content into Facebook via Twitter linking outside content. But for probably 80-90% of users, Facebook becomes the Internet.

Google doesn’t like that future. Google’s business model is predicated on having users, well, use Google. They want users to experience all the web has to offer, and to find it all through Google Search. There are two parts to that strategy: pushing the openness of the web and empowering users, and then showing them relevant advertisements. Google cannot do all the cool stuff it does without the huge revenue base it has, but more importantly, all these cool things are there to keep you on Google and learn about you so they can keep showing you relevant advertisements.

So what is Buzz? It’s a reason for people who use GMail a lot to stay on Google servers. It’s an attempt to keep users from, once they’ve read their mail, leaving the site and going to Facebook. Will it work? I don’t know. I only have access to Buzz on my Android, in the Maps program, so I can’t say how useful it is. I agree with Eric that the technology involved is cool, but that goes back to part one of the strategy: pushing openness and decentralization. (Think about Google pushing decentralization. If everything isn’t in one place, how do you find it?) But the takeaway of this is that Google is reacting to the Facebook-ization of the Internet, and it seems to be a pretty cool reaction.

Wednesday, February 3, 2010

Building Integration Tests With Workflow Components

A lot of blood, sweat, and tears have been shed over the use and design of unit tests, but what about the red-headed stepchild of automated testing, integration tests? They have fallen out of vogue lately in popular discussion, but they can be just as useful as unit tests. Just search for unit test best practices and integration test best practices; the unit test search has almost twice the results.

One of the biggest stumbling blocks I have found with integration tests is internal dependencies (external ones, like databases and webservices, are also a pain, but you’re on your own for them). If you have to test an “Approve” action, you probably first need to “Create” the object before you can approve it. You’ve written your “Create” integration tests, so do you make your “Approve” tests inherit from the “Create” tests? That seems kind of ugly. But you don’t want to have duplicate code, even if it’s just in the tests.

A better solution is to split your integration tests into “workflow” classes. Each workflow class represents an action you want to test, “Create” in this case.

public class CreateResults   
{
//data to return
}

public class CreateWorkflow
{
public CreateResults DoAction()
{
//Do the action, return the results
}
}

You can use this code for your “Create” tests, but even cooler, your “Approve” workflow can consume the “CreateResults” object:

public class ApproveResults 
{
//data to return
}

public class ApproveWorkflow
{
public ApproveResults DoAction(CreateResults results)
{
//Do the action, using the data from the create test, return the results
}
}

How cool is that?

Well, what if you want to have a workflow with various options? We can do that:

public class CreateOptions   
{
//inputs to the workflow

public class CreateWorkflow
{
public CreateResults DoAction(CreateOptions options)
{
//Do the action using the provided options, return the results
}
}

You can experiment with using the “Options” and “Results” objects in different ways. YMMV. This may not solve all the problems with integration tests (and it’s not revolutionary, and someone else probably already did it), but it can make for some pretty modular tests and make your life easier.

All rights reserved. Take that!