As you probably know from our previous blog posts, at BiteSite we recently started writing tests for our Rails apps using a combination of RSpec, factory_bot, and faker. We currently only write tests for our controllers and models. However, we are hoping to start writing some unit tests, and UI tests using Capybara.
Even though we love writing tests, we find that we can sometimes forget to run our test suite before merging code into our main branch. This can sometimes lead to failing specs making their way into our develop branch. To try and prevent this from happening, we decided to experiment with a continuous integration service, called Codeship. At first glance, Codeship seem to accomplish exactly what we wanted. However, we quickly reached and surpassed the free tier limit. Thus it was no longer running our test suite on new commits. We would eventually like to host our own CI server; however, we currently work at a shared office where we don't have control over the network setup.
This led me to come up with the idea of using Git hooks. Git hooks are scripts that are executed during a specific Git command. You can find the different types of hooks here https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks. I decided that I was going to use the pre-push hook that was introduced in Git version 1.8.2. Using the pre-push hook, I was able to write a script that ran our RSpec test suite before pushing code to our remote repositories. The Git pre-push hook will push your code if it exits with a zero status a cancel the push if it exits with a non-zero status.
At this point I wasn't sure how I was going to determine whether our test suite passed or not. So I decided to look into whether or not bundle exec rspec returned anything. Fortunately, if all the tests pass, bundle exec rspec returns a 0 if not, it returns a non-zero value. There we go! At this point I had everything that I need to create my pre-push script. Alright enough jibber jabber, let's get to the nitty gritty.
#!/bin/sh echo "Starting test suite..." bundle exec rspec if [ $? == 0 ]; then echo "" echo "================================================================================" echo "" echo "Congratulations 🎉 all specs passed!" echo "" echo "================================================================================" echo "" else echo "" echo "================================================================================" echo "" echo "😡 the tests did not pass. No push for you!" echo "" echo "================================================================================" echo "" exit 1 fi exit 0
The script was actually a lot simpler than I thought. We simply run the bundle exec rspec command and check its output status and from that we can allow or prevent the code from being pushed.
If you wanted to try this out for yourself, navigate to your project’s root folder, then into .git/hooks/. Create a file called pre-push with no extension and copy the above script into it. Now we need to make the file executable, to do so, execute the following command from your terminal: chmod +x pre-push.
Now all you need to do is push code to your remote repository and it will run the script. Unfortunately, with the current implementation, you need to wait until the test suite is complete before you can do anything else. Next time we will continue with what we started and turn the script into a background job and use mac notifications to notify us of completion status.