The current implementation suffers from a test-time dependency on an available connection to Twitter. This means the tests will run slower, they could pass or fail inconsistently, and testing error paths is too hard (how do you simulate Twitter being down?). Also, because the HTTP calls are nested inline within the functions, it's difficult to get to the information you need for debugging when things go wrong.
You might be thinking, "But I'm writing a Twitter client… don't I want to talk to Twitter during my tests?" Good question. The answer is, "As little as possible." You are not testing the Twitter API. If they expose a function for returning replies, for example, then you have to trust their implementation. What you don't yet trust—and this is the purpose of the unit tests—is your execution of their APIs and your handling of the results of those function calls. You want to test your code, not theirs.
To refresh your memory, the full code for the current TwitterClient.cfc and test code is here.
The following code illustrates the current implementation's dependency on Twitter availability:
<cffunction name="verifyCredentials" hint="Tests that the credentials are valid."> <cfset var response = {} > <cfhttp url="#getTwitterUrl()#/account/verify_credentials.#getFormat()#" method="get" username="#getUserName()#" password="#getPassword()#"> <cfset response = deserializeJSON(cfhttp.FileContent)> <cfif not structKeyExists(response,'id')> <cfthrow type="TwitterAuthenticationFailure" message="Could not log into Twitter." detail="Tried user:#getUserName()# pwd:#getPassword()#"> </cfif> <cfreturn true /></cffunction> <cffunction name="friendsTimeline" hint="returns the authenticated user's friends timeline"> <cfset var response = {} > <cfhttp url="#getTwitterUrl()#/statuses/friends_timeline.#getFormat()#" method="get" username="#getUserName()#" password="#getPassword()#"> <cfset response = deserializeJSON(cfhttp.FileContent)> <cfreturn response> </cffunction>
If the tests shouldn't test the execution of the Twitter HTTP calls, what should they test? At a minimum, they should answer these questions:
Because you're writing code in incremental steps, the TDD approach should be your ally. To that end, the tests should also answer these questions:
So how do you test your code, not theirs, while also answering all of these questions? Simply, this means removing the dependency on the external service that causes the code to be difficult to test. In practice, this means: