How to write Rspec for good?

Tram Ho

When I first used Rspec, I didn’t seem to really understand the meaning of writing Unit Test (Rspec), so I just wrote for it, or didn’t have the mind. What is the purpose of writing unit tests for, even though Google also reads the genres of the scriptures left by the seniors, but it seems that the morality is not high so I cannot enlighten. And of course, the unit test instructions in the first ROR tutorial I just learned were ignored without a bit of surprise. In the first project of my life, when rspec was required, I felt that it was quite time consuming to write this rspec, really did not know how useful it was at that time. After many times the water spilled the potato leaves, I can absorb a little bit, sometimes it is better not to write the test than to write the weak test (in a sloppy, sloppy way). Because when you’re not writing tests, you know you have to test everything, but with weak test writing you’re fooling yourself and everyone around you that it’s okay. How do you find out what test writing is weak and that we are not dependent on it?

Purpose of writing Rspec:

  • The first and most obvious thing is that you have an evindence that satisfied your boss, satisfied your customers that my Unit test covered xx% of the code.
  • Writing unit test helps to check whether the code just written is running or not.
  • Help dev list the possible cases with the code you just wrote. Thereby helping to detect cases when the code is missing, has not been thought out yet. As well as being able to reproduce the cases where the hand test has not been tested.
  • This is a pretty effective form of explanation, helping reviewers and teamates to read and understand code faster.
  • Writing good unit tests then maintaining the project will minimize the risk and save effort when after a while you just need to look at the rspec to understand the code.

Write maximum cover rate of the highest cases if possible

Ruby has a gem that is “simplecov” gem, just install it, it will assist in calculating what lines of code have been covered, which lines have not. At that time, with the goal of covering code it is easy for you to achieve a high cover percentage when you only need to test a few cases that it has covered 100% of the file you want to unit test. Some people think that this will ensure a 100% bug-free system because unit testing covers all the lines of code in the system, others think that this is just a waste of time. did not quite achieve the true purpose of writing Unit tests. Ruby is a dynamic language, so I believe that Code Coverage is a very necessary but not really enough.

Usually 100% Code Coverage is just to show that the system is 100% guaranteed free of bugs and sounds pretty good, but in reality, this only protects your system from errors. runtime.

The Rspec will still pass, Unit Test will still be good, but at Production is different, there will be data or states that you would not expect, the test case also has no case so although the Production code has Get 100% coverage but can’t cover all the cases where the bugs only the testers can detect.

Ruby is not a compiler language, it doesn’t have a compiler and lacks system checks that other languages ​​have built-in. When you execute a Ruby file, there are quite a few ways to get the Ruby interpreter to fail and stop executing, however the most common exceptions encountered while Ruby code (on Rails) are runtime exceptions.

Securing system coverage plays part of the role of a compiler, making sure the code is at least RUNNING!

Exceptions that the interpreter will immediately catch and always include MethodMissing, ArgumentError, NameError, and some other exceptions like Hash # KeyMissing from library errors or 3rd party gems errors.

Getting 100% coverage doesn’t mean your code is fully tested – the real meaning of this is that all lines of code are tested, doesn’t mean they are tested in every case.

Let’s say I have this function:

Assuming with the above code, you only need to write 2 cases a greater than 0 and a less than 0, the rate of Code Coverage has reached 100%. However, what if a is a string? . Boom “ArgumentError: comparison of String with 0 failed”. We seem to only care about the purpose of running through all lines of code and forgetting about the value type of the parameter. This omission is the potential risk of undesirable bugs in the future.

100% Code Coverage can’t speak so you write bug-free code that covers all the cases, you need to cover all C0, C1, C2 test coverage. To understand C0, C1, C2 test coverage you can refer to this article

However the difference in the efficiency of achieving 100% coverage and achieving lower ratios cannot be clearly calculated. Remember, 100% coverage does NOT mean your code is bug free, it just means that the system can only pass the most basic tests: runtime error free and executable. However, fire prevention is better than fire fighting.

For a new project we should start with a mandatory 100% coverage, which is fully controllable by the SimpleCov gem and configuring CI fails when the coverage is below a certain threshold. With SimpleCov, you can open the coverage/index.html file at the root of the system to get covered and missed lines of the system.

For projects that have existed for a long time and lacked coverage, it is extremely difficult to cover all the old code (sometimes by those who are not in the project), and sometimes do not dare to fix the old code due to it. It’s been around for a long time and doesn’t really understand how it affects old functionality. But based on the current coverage you can analyze the impact and risk of code changes. It is then possible to report back to the parties so that everyone understands the risk. We can also use additional 3rd party tools like Coveralls, CodeCov, or CodeClimate to control coverage over time and better understand what is missing and how serious they are.

Use RSpec’s shared_examples_for

Your rspec also needs to be organized in a clear and easy to understand way. In Rails, people often mention the DRY principle while coding and when writing tests too, RSpec has provided us with a tool called shared_examples_for that helps us to avoid repeating the same tests, making Our rspec file looks concise and easier to follow. To use it is extremely simple, all available on google, but the important thing is that we accept to learn or not only. Assuming in my application there are 2 models that are User and Project, both exist relation for follow function. Relation of relation and follow-up are written in Followable module.

To be more specific, let’s create a simple example. I want to describe the behavior of different dogs. Each type of dog can perform some common actions, but also certain actions they cannot perform. For example, Snuff can bark and growl, but doesn’t like to dance. On the other hand, Scooby-Doo likes to dance and run, but doesn’t growl. The Scrappy-Doo is too small to bark, but it loves to growl.

Now we will write the rspec for each action for each dog into a common function:

Now I will write rpsec for each dog:

Suppose if I do not use shared_examples per shared_examples of a dog, I will write Snuff:

It’s long, isn’t it if I use shared_examples it will be much shorter:

We need to gather the same test pieces, just copy this place and put it elsewhere as in the above example, making our test pieces lengthy. ✌️ So how to avoid duplicate tests, let’s see how I use that guy shared_examples_for to do it. We will create a directory for writing reusable tests. You can say this guy is similar to a module in Rails.


We should write Rspec as strictly as possible, not only writing to pass 100%, but making sure to cover all cases. Writing rspec so that it is easy on the eye does not make reviewers bored when pushing up the code. Have a nice weekend !


Share the news now

Source : Viblo