Unit testing from a different perspective

Akhil Lb
Drip Capital
Published in
4 min readFeb 6, 2020

At Drip Capital, we maintain unit tests for most of our core products. This helps us make changes to our business logic with more confidence, without worrying if any of these changes would affect other parts.

Recently, we had to integrate an external REST API service into a new product that we were building. We usually upload a resource on this system using one of their HTTP POST-APIs. It would process these uploaded resources and share that resource with our user. When a user performs any kind of updates on this resource, we would receive those updates on to our backend systems via callbacks.

The biggest hindrance arises when we would want to dev-test the code that integrates this service into our system. The callback URL needs to be on a network that can be accessed by this external service that we are integrating. We could use something like ngrok, but this external service requires you to whitelist the host. So every time we restart the ngrok process, we will have to update the whitelist with the new ngrok host. So every developer who will work on this needs to be granted access to the dashboard to whitelist the URL.

This is where we decided to use Unit tests in a new way to dev-test our integration. The idea was to stub the requests to this service while we ran all the unit tests for the functions which would call this service and check if it is working as required by the specs.

Stubbing the requests

We added the gem webmock and started stubbing all the requests that we have been making to the application in our test cases.

Stubbing application request to test uploading resource

To test the callback we just had to write a controller spec.

Testing callbacks

These very small lines of code saved us a lot of time, which we would otherwise have wasted in setting up the application, getting the access token, refreshing it, whitelisting the new URL generated by ngrok, etc.

Other benefits of this approach

There were other side benefits in doing this as well. For example, testing different scenarios. The resource that we uploaded can transition into multiple states, each state depending on many factors. If we were to integrate this manually, for every case we would have had to upload a resource and then try to change the state of the resource manually and verify if our my code is behaving as it is supposed to or not. We would also have to repeat the same process when we find that it is not working as intended and make changes to it.

In this approach, it becomes much easier than the above method. We can stub the request to return an HTTP 500 error to see if the case is failing. We can send any required value to the resource’s status in the controller spec for the callback and verify the status. This again saved us lots of time.

Spec to handle upload failed

We also experienced the obvious benefits of Unit testing. We were more confident about our code. It helped us rewrite our code so that it can be unit tested, which made our code more efficient and also easy-to-read. Obviously, with these test cases in place, it would be easy when people decide to refactor, as they safeguard the business logic and also, in a way, serve as documentation.

Extending this approach

We started applying the same approach for other external services in our project too. We even started doing this for functions from other libraries that we have been using. It saved us a lot of time while testing the backend login implementation for the product. The login is supposed to return different responses if the user has already signed in once, or the user is signing in for the first time or if the user has just signed up. Testing this became easier because for each case it was just two pieces of code and we just had to write it once. Every time we changed or added anything to the login module, we just had to run these test cases and we would be done. With the manual testing approach, we would have had to log in for each case and then log out and try with the next one.

TDD is always good?

This was valuable learning because we saw great benefits for writing Unit tests. However, from our personal experience, we are not sure if this would be a benefit all the time. With this new learning, we decided to start writing unit tests for new projects that were kind of in a research phase. It did not have any external dependencies. We thought that if and when the project grew bigger these unit tests would be of great help. We also were relying on these unit tests to serve as documentation. The issue with this was that it was just in an exploratory phase. We were changing the entire architecture too fast too many times and with such heavy rewrites, we had to rewrite our test cases as well. So in this case, test cases actually kind of became a burden at such an early phase. So we decided to start writing unit tests once a project reaches a certain mature state.

Conclusion

This article is just to present our learning writing Unit tests. It does not conclude in any way that writing Unit tests are a better methodology or not. The impact it has on productivity, in our experience so far, completely depends on the type of project we are working on, the stage in which the project is and the developer. It would take a person with more experience in writing test cases less time than a person who has relatively less experience.

I hope this article will help fellow devs make a better decision when deciding whether or not to write unit tests for your work.

--

--