Wrapping a telephony RESTful service for sending text messages and making phone calls So I'm writing an abstraction layer that wraps a telephony RESTful service for sending text messages and making phone calls. I should build this in such a way that the low-level provider, in this case Twilio, can be easily swapped without having to re-code the higher level interactions.

I'm using a package that is pre-built for Twilio and so I'm thinking that I need to create a wrapper interface to standardize the interaction between the Twilio service package and my application. Let us pretend that I cannot modify this pre-built package.

&lt;?php

namespace Telephony;

class Provider_Twilio implements Provider_Interface {

    public function send_sms(Provider_Request_SMS $request) {

        if (!$request-&gt;is_valid())
            throw new Provider_Exception_InvalidRequest();

        $sms = \Twilio\Twilio::request('SmsMessage');
        $response = $sms-&gt;create(array(
            'To' =&gt; $request-&gt;to,
            'From' =&gt; $request-&gt;from,
            'Body' =&gt; $request-&gt;body
        ));

        if ($this-&gt;_did_request_fail($response)) {
            throw new Provider_Exception_RequestFailed($response-&gt;message);
        }

        $response = new Provider_Response_SMS(TRUE);
        return $response;

    }

    private function _did_request_fail($api_response) {
        return isset($api_response-&gt;status);
    }

}


The idea is that I can write another file like this for any other telephony service provided that it implements Provider_Interface making them swappable. Here are my questions:


Do you think this is a good design? How could it be improved?
I'm having a hard time testing this because I need to mock out the Twilio package so that I'm not actually depending on Twilio's API for my tests to pass or fail. Do you see any strategy for mocking this out?
In circumstances like you have here; the almost lowest level layer wrapping a dependency, it can be hard to see another layer lower to allow abstraction, but remember this: The lowest level wrapper of a dependency is a literal straight pass-through.

This means, if you take the twilio api and create a direct correlary that simply passes-through everything to the twilio api, then that wrapper is the lowest level wrapper and therefore bears no need of testing at the unit level (testing at the integration and build-verification levels however is worth while) as it has no actual functionality.

If you create such a wrapper, you may then pass in a mock of it to your next-level-up which is your provider here.

So i guess there are two rules I'm stating here, and glad to hear people give points for or against:


The lowest level wrapper possible of external dependencies is a direct pass-through (which feels stupid and tedious when writing it, but actually serves a purpose)
The lowest level wrapper of an external dependency has no need of testing at the unit level as it maintains no testable functionality, but should be tested at the integration and build-verification levels as well as all external dependencies should.


Also, for your particular case and similar ones, you have an obvious default implementation of the dependency that you will be wrapping so feel free to have a default constructor so its consumer's do not need to construct the twilio wrapper, while maintaining the ability for the twilio wrapper to be settable for the unit tests. Sometimes making the accessibility of these setters protected enough that consumers will not think it's ok to change it, while accessible enough for proper unit testing can be tricky; I tend to treat those situations with a use your best-judgement approach.

Edit:
I would unit test the Provider_Interface. Though that's one man's opinion, many people would say unit testing at this level is stupid, I maintain that where there is functionality, which your Provider_Interface has, there may be bugs. You could have a typo in 'SmsMessage' for instance, and therefore should unit test for these things. However I acknowledge there is much disagreement in industry about the ROI on unit testing at this level, or maintaining pass-through wrappers for such purpose as I am advocating.