Back to Testing

Ruby Bautista
5 min readDec 1, 2020

It’s been far too long since I revisited the topic of testing. This week, we added testing to our link checkers. I had some trepidation, but I was also a bit eager to delve into the topic. As arduous it can be to write tests, having to manually test your software also gets to be even more arduous over time. I have spent an hour mindlessly manually testing my program only to space out from boredom and have to go through the processes all over again.

There were different ways to test in python, I decided to use unittest; the one already included with the Python standard library. It’s inspired by JUnit and having a little bit of experience with JUnit, I decided to give it a try, and with it being included in Python, it would be one less dependency.

Set up was very simple, since unittest was already added to Python, all I had to do was import it into a new python file:

After importing the unittest, I then created a new test class that inherited from the unittest.TestCase. The tests, unlike in JUnit, didn’t have a Test annotation but was instead defined as a regular function that took “self” as a parameter. Similarly to JUnit, values evaluated using assert like so:

One thing that I was concerned about was how to replicate the HTTP responses so that I would always be guaranteed that the url I’m passing to the tested function returned the expected response. This was done through patches which were defined by annotating the function with @unittest.mock.patch

#this redirects the output from the terminal		
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
#this mocks the HTTP responses
@unittest.mock.patch("linkreaper.urllib3.PoolManager.request")
def get_website_response_good(self, mock_request=None, mock_stdout=None):

I got stuck quite frequently when implementing this… 😅. This was the first time I had done anything like this and I kept getting issues with how mock_request and move_stdout were being passed in. But, eventually, I figured out how to get it to work. So when I finally was ready to commit…Pylint decided to complain 😓.

I ran into a couple of limitations with Pylint in this project. One. is that it doesn’t realise that mock_request and mock_stdout were not being passed into the function by the caller. This necessitated the addition of a default value as shown above. Secondly, is that since I was using mock in the function annotation, it thought that the import was unused and therefore unnecessary. After some browsing around StackOverflow and Github, I finally came across a way to get pylint to ignore the unused import:

This was good to know. As many benefits pylint provides, there are still small cases like this where its behaviour needs to be overwritten or ignored.

When I was testing, I did realise that were a couple of cases that I had completely missed. For example, my output codes function is the one responsible for getting the website response and subsequently, printing it out. I found that there some issues with the if-else block and changed it right away. After testing my code…I am beginning to thinking that it would be better to separate the retrieval of the website responses from the printing of the output. It works fine with how it’s called from the command functions, but in isolation, it seems pretty convoluted.

Code coverage was done with… Coverage; truly an apt name. Unexpectedly, this is the part of the lab that gave me the most grief. While the tests ran normally in my IDE it would have some issues running with Coverage. I didn’t know why, and I still don’t know why. However, eventually, I was able to get the coverage running after putting an “init.py” in both the tests and linkreaper directories. I also decided to rename linkreaper from linkReaper. My habit of using camel case was really not playing well with my experience with pylint. Once I finally had coverage running and I was able to get the HTML report, I was able to see what lines were missing coverage. There were still quite a few lines to test for. A good number of them were the Click commands. Upon further research on how to test the click commands, I found out that Click had its own way of testing. I’ll certainly integrate this testing method in the future. I didn’t realise just how fleshed out this library is.

CI I confess was the one I was most eager to learn how to implement. I have been guilty in the past of introducing an error that would have completed broken a group project. It was only because of intuition that I decided to recheck the project, that I was able to catch it early. I can’t rely on gut feeling all the time, especially when dealing with production code. And we can also see this in the course. What builds on another’s setup may for some inexplicable reason not run on another person’s (For example, Matrix and Visual Studio). With CI there is at least some level of comfort that the code isn’t completely broken. Therefore, learning CI is something that I’ll be sure to integrate into my repositories from now on.

Embarrassingly, too much time was spent looking for the .github folder until my sleep-addled mind finally remembered that it was accessed through the “Actions” tab. The process of setting it up was easier than expected. After a couple of rechecking and modifying my code to meet the workflow’s demands. It was up and running and checking all the pushes into the master branch.

Working on haystack

My partner’s tests were also written in unittest so I was able to apply my new knowledge to contribute to his project. One of the major things that I did was to mock the HTTP requests as I had described above. With his project, it was a bit simpler given that the function that retrieved the HTTP codes had that sole responsibility and it is also partially the reason why I am considering refactoring my code. I modified his code to use the mock rather than getting the actual HTTP responses as well as added a new test for testing unknown code.

I think that I learned a fair amount from this lab. One of the things I really appreciate with this class is how relevant the things we learn are. I am able to use this knowledge right away in all of my projects and with any language. I really appreciate that aspect. I will most definitely be applying what I learned in the future.

--

--