This is a little post about how I got some tests up and running on the YouTube v3 API in a Symfony application.
This post also applies to Symfony since I’m creating an angularjs app that will use Symfony as its back-end API. In turn, this will call a load of data providers including YouTube. For my unit tests, I needed to mock the YouTube google services’ search method. For functional tests in Symfony I needed to use the DIC to inject my test YouTube classes.
To add the YouTube service to a symfony install, it can be added to the composer.json file and then referenced from any class (here are the official installation instructions).
//composer.json "require": { "google/apiclient": "1.0.*@beta" ...
Unit tests
To unit test the search functionality, this is an extract of my YouTubeProviderTest.php file…
//1 $this->gsYouTube = $this->getMockBuilder('\Google_Service_YouTube') ->disableOriginalConstructor() ->getMock(); //2 $this->gsYouTube->search = $this->getMockBuilder('\Google_Service_YouTube_Search_Resource') ->disableOriginalConstructor() ->setMethods(array( 'listSearch' )) ->getMock(); //3 $this->gsYouTube->search->expects($this->any()) ->method('listSearch') ->will($this->throwException(new \Exception())); //4 $emptyFile = 'sampleEmptyYouTubeListings.txt'; $this->gsYouTube->search->expects($this->any()) ->method('listSearch') ->will($this->returnValue(json_decode(file_get_contents($emptyFile),true)));
- The first bit mocks the actual YouTube service, disabling the original constructor. If this isn’t done, a valid Google_Client object needs to be passed to it.
- The Google_Service_YouTube object contains a public ‘search’ property, which itself is an instance of the ‘Google_Service_YouTube_Search_Resource’ class. Since its public, its pretty easy to set this to a mock object as well. Its a class with only 1 method, namely, ‘listSearch.’
- This statement says that whenever the ‘listSearch’ method is called, throw an exception.
- This statement says that whenever ‘listSearch’ is called, return the file ‘sampleEmptyYouTubeListings.txt’ which is a simple json string.
Functional testing
To set up the DIC such that the test environment will automatically use a test version of the YouTube service, I did the following…
All my data providers are themselves inside a service called ‘MediaProviderApi.’ For the YouTube service, inside the ‘services.xml’ file, I’ve got the following…
<!-- 1. google services--> <service id="google_client" class="%google_provider.class%" public="false"> <call method="setDeveloperKey"> <argument>%google_provider.gdata_key%</argument> </call> <call method="setApplicationName"> <argument>%google_provider.gdata_app_name%</argument> </call> </service> <!-- 2. youtube services--> <service id="google_service_youtube" class="%youtube_provider.google_service_youtube.class%" public="false"> <argument type="service" id="google_client" /> </service> <!-- 3. youtube provider api --> <service id="youtube_provider" class="%youtube_provider.class%" public="false"> <argument type="service" on-invalid="ignore" id="google_service_youtube" /> <argument id="sonata.cache.apc" type="service" /> </service>
- The Google_Client service can be constructed here. By parameterising the class, its also possible to pass a test class in here too, although I’ve not needed to do that. Notice the ‘call’ statements. To create a valid Google_Client object, the google data key and application name has to be set. The values above are injected from either the config.yml or config_test.yml
- To construct a valid Google_Service_YouTube object, a valid Google_Client must be passed to it. This is done here. By marking the service as ‘public=false’, it means it can’t be directly accessed through the container object. Since the class is parameterised, a reference to a test class can be made from the config_test.yml file.
- Finally, the actual youtube provider service is created using the google_service_youtube service. My provider also uses sonata’s APC cache service but that’s beside the point 😉
Service configuration
To make things a bit cleaner, in the configuration.php for the service, I add a default value to the YouTube service class. This means that its only the test class that needs to be defined when necessary.
//DependencyInjection/Configuration.php //... ->arrayNode('youtube_provider') ->isRequired() ->children() ->arrayNode('google_service_youtube') ->addDefaultsIfNotSet() ->children() ->scalarNode('class') ->defaultValue('Google_Service_YouTube') ->end() ->end() ->end() ->end() ->end()//end of youtubeapi
So, in the config.yml file, its only required to define the YouTubeProvider like so…
sk_media_api: media_provider_api: debug_mode: false providers: google_provider: gdata_key: %gdata_key% gdata_app_name: %gdata_app_name% youtube_provider: ~
In config_test.yml, the test class is defined
sk_media_api: media_provider_api: debug_mode: true providers: youtube_provider: google_service_youtube: class: Sk\MediaApiBundle\Tests\MediaProviderApi\TestGoogleServiceYouTube
So, in the functional tests, the DI container will load the test google youtube service class so that my YouTube quota isn’t immediately used up.
Hope that helps someone…
Hi Simon,
I am reading this interesting post much of what is written is clear. However is it possible for you to give the entire code along with the tests that you did through github or even via email ? I am still learning these webtechnologies, I am a student. I dont have any prior experience in web development but I want to use symfony framework for an application I am building that would require youtube interaction at the backend. Can you send me your code on this post http://justthisguy.co.uk/testing-youtube-api-v3-php-symfony/ ?
It would be nice to see the entire code, so I can learn from it the organization of how to use a generic php project and fit it in the symfony framework, a task which I am a bit struggling at. If you get time please send the code, thanks in advance.