Same client as in a refactoring writeup, but this time I was refactoring out some un-necessary calls through the browser and replacing them with hooks into an API that is available.
Whenever it makes sense to do so, you should try to not do things in the browser. Yes, it is one of those great ironies of writing Selenium scripts. Here was my report on it.
I have removed the ClientPage.get_validated_random_search_term() method and replaced it with a call to the REST API. This call was pulling a term from a csv and trying it as a search term. If a video was returned with that term, then that term was returned. Now, if there wasn’t an API available is a clever solution. But we have one. :)
So lets have a look at this. Here is the hook to actually get the information from the server. Notice how it doesn’t do any transformation. It is the raw response.
import json import requests from ClientException import APIException import clientrest from saunter.ConfigWrapper import ConfigWrapper as config class VideoHub(clientrest.ClientRest): @classmethod def popular_videos(cls): url = "/rest/videohub/popularVideos?output=json" r = requests.get(config().config.get("Selenium", "base_url") + url) if r.status_code != 200: raise APIException("%s response from %s" % (r.status_code, url)) return json.loads(r.content)
Any transformation/filtering is done a layer higher. In this case we only care about the tags to be used as search terms.
import random import os.path import clientrest.videohub class SearchTerms(object): @classmethod def random_video(cls): search_terms =  popular_videos = clientrest.videohub.VideoHub.popular_videos() for video in popular_videos["videoList"]: search_terms.extend(video["tags"]) return random.choice(search_terms)
Which means we can just do
search_results_page = self.video_dasboard_page.perform_search(SearchTerms.random_video())
or if we need to recycle that same term
search_term = SearchTerms.random_video() search_results_page = self.video_dasboard_page.perform_search(search_term)
The key thing here is the separation of concerns. One class deals only with the interaction with the API and does nothing specific around how the information will ultimately be used. That is the responsibility of a different class which will likely grow random_photo, random_music and random_radio when we need them. Other uses of the returned API information would be in their own, separate, class. And by structuring things in this manner, if (when) the API changes or how we determine the ‘tags’ changes, the script is isolated from this and should not have to modify it.