On my first software development project

For those of you who are connected to or following me on LinkedIn, you might have noticed that recently, I’ve shared and reacted on quite a few updates related to TestProject and their new and open source SDKs, especially the brand new Python SDK. Why? Because I built that Python SDK, and because I’m pretty damn proud of the end result.

I’m not going to talk about how the SDK works in this post, I’ve written a series of tutorials available on the TestProject website that do exactly that. If you want to find out more about the SDK, what it does and how to get started, please read this tutorial. This post, instead, is more of a reflection on the process and what I’ve learned from working in a software development role for the first time in my career.

How did this project get started?
I’ve known and worked with the team at TestProject for a while now, ever since my first blog post was published on their website back in October of 2018. I’ve always enjoyed working with them, and together we’ve published some 10 articles on blog.testproject.io, not including the SDK tutorials. Then, late December of last year, they got in touch to see if I was interested in collaborating with them on a to-be-developed Python SDK for their test automation platform that would supplement their existing Java and C# SDKs.

Initially, I declined. Having no real experience as a software developer, plus having a training business that was growing pretty rapidly, I thought it wasn’t the right fit at that moment. However, the opportunity of experiencing first-hand what it would be like to be a developer, and moreso, a developer for a free and open source project that would benefit the software testing community, kept flying around in my head, until I was sure that saying ‘yes’ was the only right thing to do. Lucky for me, the job was still open, so I accepted, we worked out the terms and conditions, and that’s when the project could actually get started.

What did I learn in the process?
To be honest, I don’t think there’s been a single project in my career where I’ve learned (had to learn!) as many things in such a short time as with this project. Granted, I knew a thing or two about writing code, about Python and definitely about test automation, but going from there to actually creating a software product and making it available to the outside world was definitely a step (or two, or three …) up from that. Take these things, for example:

  • Writing code that is well-structured enough to allow easy unit testing
  • Packaging and publishing code to a public repository
  • Effectively applying code linting and formatting tools
  • The intricacies of Git – branching, rebase with autosquash, fixup commits, dealing with merge conflicts, … (I’m sorry for all the frustration I caused, Marat and Vitaly!)

And that’s only the general software development skills.. I’ve learned so much about Python, the way it works and what you can do with it, too, the list would just be too long to include here.

Any other lessons learned?
Oh, lots of them! The biggest lesson, probably, would be to not underestimate again the time and effort it takes to create and release a quality product. I knew this, of course, having worked in software development teams for years, but it has never been as tangible and as first-hand an experience as with this project.

Another lesson is that yes, developers do make mistakes 🙂 At least I did… Lots of them! I’m lucky to have had the opportunity to work with a couple of great testers in this project, who came up with excellent ways in which the thing I built did not work as expected, even though I thought I covered them all.

Even though I’ve been on the other side of these discussions a lot in the past (being a tester, pointing out what I thought was a bug to a developer), seeing and experiencing this from a developer perspective has been a really valuable experience. Thank you so much for that, Ran and Gil!

Would I do it again?
Without a doubt: yes! Even though test automation is where my heart -and my experience- is, I loved the experience of actually creating something, seeing it go live and reading about others using and appreciating it.

I don’t think I’ll ever turn into a full time developer (never say never, though), if only because I like delivering training just as much, but I do think it’s a great way to spend part of my working hours. In fact, the next development project is already in the works, and I hope to be able to do this even more often in the future.

The end result
If you want to have a look at the end result of this project, it’s available on GitHub here. Why not give it a spin and let me (and the TestProject team) know what you think? Feel free to leave a review, file an issue if you think something’s not right or missing, or even better, submit a pull request.

A big, big thank you once again to the entire team at TestProject for making this an amazing experience. I won’t name all of you individually for fear of accidentally leaving someone out, but you know who you all are 🙂

TDDing my way to a Python singleton implementation

So, I’ve been working on my first ever project as a software developer for a while now, and it’s crazy (but amazing) to see how much I have learned so far. Anything from writing readable code, to unit testing, to creating good documentation, to packaging and publishing my work, you name it, I’ve done it now. And I’m still learning, a lot, every day. So far, I’m loving it.

One of the challenges I encountered along the way was that I had to implement a singleton in Python. For those of you who don’t know what a singleton is, it’s a pattern that restricts the instantiation of a class to a single instance. I need this in my software because I need a single source of truth for a number of settings that are valid for the duration of a session.

Now, there are plenty of solid examples of how to do that in, for example, Java (see here for two examples as explained on the excellent Baeldung website), but for Python, not so much. It’s not that there aren’t any examples available online, the problem is that there are a lot of them, all implemented in different ways, and not all of them are equally readable and portable to what I needed. I’d prefer a native solution (no relying on third party libraries), since I’m developing a package for distribution myself and the fewer dependencies, the better.

So, I thought, why not try my hand at Test Driven Development (TDD) and see where that leads? It’s a practice I would like to have more experience in anyway.. In this blog post, I’ll try and explain my thought process and what I came up with in the end. It might not be the best implementation (hey, I’m still learning, and this is my first ever attempt at TDD), and I’d love any feedback and suggestions, but it did the trick for me in tackling this challenge.

Let’s go! (This is me talking to myself for the remainder of this blog post)

The first requirement for my singleton class implementation (let’s call it MySingleton) was that it could be instantiated. The simplest test that checks this requirement looks like this:

def test_singleton_can_be_instantiated():
    my_singleton = MySingleton()
    assert my_singleton is not None

and here’s the simplest version of the class code I could think of that makes this test pass:

class MySingleton:

    def __init__(self):

So far, so good. Our class is not a singleton yet, of course. Also, it’s not a very useful class… Before we move to the actual singleton part, let’s make sure that we can pass a parameter to the __init__() method (comparable to the class constructor in other languages), that it is stored in a field and that it can be read again. First the test:

def test_singleton_can_take_an_argument():
    my_singleton = MySingleton(fruit='banana')
    assert my_singleton.fruit == 'banana'

then the implementation:

class MySingleton:

    def __init__(self, fruit: str):
        self.fruit = fruit

Not a lot of refactoring that can be done here, I guess, so let’s move on to the next step. Now that we have warmed up, it’s time to take a shot at making this class a proper singleton. And here’s where things get interesting…

Typically, a singleton is created by ‘hiding’ the constructor (i.e., making it private), but Python does not allow us to do that. So, we need to find a way around it.

What we can do, though, is creating a class variable instance that contains the instance, and assign the current instance of the class to it whenever the __init__() function is called for the first time. A test for this would simply have to invoke the __init__() method by calling MySingleton() and then check that the instance class variable is not equal to None:

def test_singleton_instance_is_stored_as_a_class_variable():
    my_singleton = MySingleton(fruit='pear')
    assert my_singleton.instance is not None

We can implement this requirement like this:

class MySingleton:

    instance = None

    def __init__(self, fruit: str):
        MySingleton.instance = self

But hey… didn’t we forget something here? Yes, indeed, we also need to make sure once more that the fruit field of our single instance is set and can be read. Here’s the test:

def test_singleton_class_variable_exposes_properties():
    my_singleton = MySingleton(fruit='apple')
    assert my_singleton.instance.fruit == 'apple'

and here’s the implementation:

class MySingleton:

    instance = None

    def __init__(self, fruit: str):
        MySingleton.instance = self
        MySingleton.instance.fruit = fruit

Phew. Close call! Now would be a good time to check if there’s any refactoring we can do on our class.. Nope, so far so good I think!

Next, I’d like to add another requirement: because we can’t hide the __init__() function, I’d like to get notified whenever a process calls this function after the singleton has been created. You never know who’s going to consume your class.

I could choose to simply ignore any subsequent calls to __init__(), but instead, I’d like to be explicit and raise a RuntimeError whenever __init__() is called after our instance has been created.

Here’s what a test for that could look like (notice that testing for raised errors is straightforward with pytest):

def test_singleton_cannot_be_instantiated_twice():
    with pytest.raises(RuntimeError) as re:
    assert str(re.value) == 'Already instantiated!'

and here’s the implementation to make our test pass:

class MySingleton:

    instance = None

    def __init__(self, fruit: str):
        if self.instance is not None:
            raise RuntimeError('Already instantiated!')
        MySingleton.instance = self
        MySingleton.instance.fruit = fruit

As a final requirement, instead of allowing consuming classes to refer to the instance class variable directly, I’d like to encapsulate access to it in an instance() method, so that in the future, we can put additional logic or validations in there if required.

Because we might want to allow for class modification in this method in the future, it’s best to make this method a class method, as static methods are restricted in what data they can access.

We have to test for a couple of things here:

  1. The instance() method should return the initial instance
  2. The instance() method should allow access to the fruit field, and
  3. The instance class variable (renamed to __instance) should no longer be directly accessible (since that would break the encapsulation)

Here are the tests that check whether all the above conditions are satisfied:

def test_singleton_retains_initial_property_values_after_subsequent_init_calls():
    with pytest.raises(RuntimeError):
    assert MySingleton.instance().fruit == 'lychee'

def test_singleton_instance_is_accessible_using_class_method():
    my_singleton_instance = MySingleton.instance()
    assert my_singleton_instance.fruit == 'raspberry'

def test_singleton_instance_field_is_not_directly_accessible():
    with pytest.raises(AttributeError) as ae:
    assert str(ae.value) == "'MySingleton' object has no attribute '__instance'"

and here’s the final implementation of the MySingleton class:

class MySingleton:

    __instance = None

    def __init__(self, fruit: str):
        if MySingleton.__instance is not None:
            raise RuntimeError('Already instantiated!')
        MySingleton.__instance = self
        MySingleton.__instance.fruit = fruit

    def instance(cls):
        return cls.__instance

Great! Our singleton is ready for use (again, I don’t think this needs a lot of refactoring at the moment), and we’ve got a number of tests to go with it, all thanks to a little bit of ‘thinking out loud’:

What do I want my code to do? How can I test for that?

For me, this has been a useful first go at applying TDD to write production code. I’ve certainly learned a lot in the process. I’m not sure if I’m going to use it all the time, but I am definitely going to practice it some more in those cases where I can’t exactly figure out how to implement something right away.

All code snippets and tests are available on my GitHub page, feel free to check them out here.

P.S. I’m looking for my next opportunity as a freelance Python developer (relatively new but learning fast), or as a freelance test automation trainer or engineer (experienced), from mid June 2020 onwards. I’m currently working on a Python development project and would definitely love to continue down this path. Feel free to get in touch or refer me to somebody you know who might be hiring. Thanks!

Writing tests for RESTful APIs in Python using requests – part 4: mocking responses

In this short series of blog posts, I want to explore the Python requests library and how it can be used for writing tests for RESTful APIs. This is the fourth blog post in the series, in which we will cover working mocking responses for unit testing purposes. Previous blog posts in this series talked about getting started with requests and pytest, about creating data driven tests and about working with XML-based APIs.

One thing that has been keeping me busy in the last couple of months is improving my software development skills. I’m currently working on a Python development project, and one of the tasks of a developer is writing good unit tests. When I was writing these tests, I ran into a challenge when I wanted to test a method that involves communicating with a REST API using the requests library.

Obviously, I don’t want to have to invoke the API itself in my unit tests, so I was looking for a way to mock out that dependency instead. One thing I considered was writing mocks for the API myself, until I stumbled upon the responses library (PyPI, GitHub). According to their homepage, this is ‘A utility library for mocking out the requests Python library’. Exactly what I was looking for.

So, what can you do with the responses library, and how can you use to your advantage when you’re writing unit tests? Let’s look at a couple of examples that involve creating mock responses for the Zippopotam.us API.

Creating a mock response
Let’s say that in our unit test, we want to test that our code handles an HTTP 404 returned by a REST API dependency as expected. This implies we need a way to ‘override’ the actual API response with a response that contains an HTTP 404 status code, and (maybe) a response body with an error message.

To use the responses library to create such a mock response, you’ll first have to add the @responses.activate decorator to your test method. In the test method body, you can then add a new mock response as follows:

def test_simulate_data_cannot_be_found():
        json={"error": "No data exists for US zip code 90210"},

When you use the requests library to perform an HTTP GET to http://api.zippopotam.us/us/90210, instead of the response from the live API (which will return an HTTP 200), you’ll receive the mock response, instead, which we can confirm like this:

response = requests.get('http://api.zippopotam.us/us/90210')
assert response.status_code == 404
response_body = response.json()
assert response_body['error'] == 'No data exists for US zip code 90210'

You can add any number of mock responses in this way.

Unmapped responses
If, during testing, you accidentally hit an endpoint that does not have an associated mock response, you’ll get a ConnectionError:

def test_unmatched_endpoint_raises_connectionerror():
    with pytest.raises(ConnectionError):

Simulating an exception being thrown
If you want to test how your code handles an exception being thrown when you perform an API call using requests, you can do that using responses, too:

def test_responses_can_raise_error_on_demand():
        body=RuntimeError('A runtime error occurred')

You can confirm that this works as expected by asserting on the behaviour in a test:

with pytest.raises(RuntimeError) as re:
assert str(re.value) == 'A runtime error occurred'

Creating dynamic responses
If you want to generate more complex and/or dynamic responses, you can do that by creating a callback and using that in your mock. This callback should return a tuple containing the response status code (an integer), the headers (a dictionary) and the response (in a string format).

In this example, I want to parse the request URL, extract the path parameters from it and then use those values in a message I return in the response body:

def test_using_a_callback_for_dynamic_responses():

    def request_callback(request):
        request_url = request.url
        resp_body = {'value': generate_response_from(request_url)}
        return 200, {}, json.dumps(resp_body)

        responses.GET, 'http://api.zippopotam.us/us/55555',

def generate_response_from(url):
    parsed_url = urlparse(url).path
    split_url = parsed_url.split('/')
    return f'You requested data for {split_url[1].upper()} zip code {split_url[2]}'

Again, writing a test confirms that this works as expected:

response = requests.get('http://api.zippopotam.us/us/55555')
assert response.json() == {'value': 'You requested data for US zip code 55555'}

Plus, responses retains all calls made to the callback and the responses it returned, which is very useful when you want to verify that your code made the correct (number of) calls:

assert len(responses.calls) == 1
assert responses.calls[0].request.url == 'http://api.zippopotam.us/us/55555'
assert responses.calls[0].response.text == '{"value": "You requested data for US zip code 55555"}'

Using the examples for yourself
The code examples I have used in this blog post can be found on my GitHub page. If you download the project and (given you have installed Python properly) run

pip install -r requirements.txt

from the root of the python-requests project to install the required libraries, you should be able to run the tests for yourself. See you next time!