Orange is my favorite color

I’ve been doing a lot of development of API libraries over the past year while integrating remote services into MotorsportReg.com. I struggled with how to best perform unit tests to verify the code works locally as well as integration tests to know it works with the remote API which we don’t control.

The problem is you don’t want to (or can’t…) always send every request over the wire to the remote API. You might develop offline, the remote API might have limits, or real requests may take a long time which is not practical for rapid testing. Sometimes we want to use a mock response and other times we want to connect to the third party to make sure things function properly. How do we write unit tests and integration tests without duplicating our work?

Conditionally Using Mocks

A “mock” is an arbitrarily defined data structure or object that mimics behavior. MxUnit describes mocks providing “the ability to easily and quickly define behaviors for dependencies” which allow us to test more discretely without worrying about dependent data structures, objects or behaviors.

Since the concept of an integration test is different for a headless client library, my approach has been to use MxUnit’s injectMethod() to conditionally override a method for mocking:

<cffunction name="offlineInjector" access="private" hint="conditionally injects a mock if we are running tests in offline mode vs. integration mode">
  <cfif localMode>
    <cfset makePublic(arguments[1], arguments[4]) />
    <cfset injectMethod(argumentCollection = arguments) />
  </cfif>
</cffunction>

localMode is defined in the setup method and is a simple boolean. I replace all usage of injectMethod() to instead use my offlineInjector():

<cfset offlineInjector(clientsvc, this, "anExampleListMock", "myMethodToReplace") />

Each time the test is run, the method will be mocked only if localMode is true. Otherwise, the client library will use connect to the remote API to execute the request. I do most of my development with localMode = true and set it to false when I’m ready to verify everything works both locally and remotely.

There are probably many ways to skin this cat and this has been working well for me without duplicating test code. I used it most recently to wrap up integration with EmailCenterPro and their REST API which lets us use templated emails to send service agreements and welcome new customers as part of our sales process.

Comments are closed.