On my journey on , one of the things that troubled me the most to understand was RSpec, a Ruby gem for Test-Driven Development. The concept is quite simple. You create tests as you code along, so in the future, if an update breaks something, it will be easy to notice because one or more tests will fail. Microverse If you don't have RSpec installed on your machine, to know how to get it. Aside from the installation, it explains how to apply RSpec to a file, and it also gives some details about the tests' output. follow this guide The first steps were easy to follow, but one challenge arose: testing user input. Since I don't want to lose the knowledge I just acquired, and I had trouble finding guides on this matter, I want to make my life and the others easier writing this article, so I can use it later as reference, or at least, a quick reminder. Scopes RSpec's syntax lets us create various scopes which we can use to share variables through tests. RSpec.describe Class describe it it describe it RSpec.describe DifferentClass do # Declare variables that are readable by all tests of this class '#method' do # Declare variables that are readable by all tests of this method 'nice description of what this test is testing' do # Test especifications end 'you can have more than one test per method!' do # Different test especifications end end '#another_method' do # You can describe as many methods as you want # inside a class! 'another_methods test' do #Especifications end end end do # Create different scopes for each class! end Notice that on , isn't a string. The method defines each test you want to implement. Also, it is possible to share objects and tests across scopes. for more about this feature. RSpec.describe Class do Class it Check the documentation Variables RSpec provides the method for variable declaration. The traditional way would also work, but creates the variables only when required by the tests, which saves memory and accelerates the computing process. You can also use if you want the variable loaded before the test execution. let let let! let( ) { Class.new } var = Class.new # These two methods of variable declaration are analogous :var The variable name must always be a symbol in the method, and its content can be any or object. let primitive data type let( ) { double( ) } var = double( ) # It also works for doubles :var 'class' 'class' The method creates a generic object, to which you can attribute behaviors (even though you can do that for real objects too). The string is descriptive and doesn't link the object to any class in specific. double() 'class' The Example Code From this point on, we'll start to write tests for two classes: and . For convenience, I will display both the method tested and the test in the same block of code, but in your work, you should have a file dedicated to the core program, and another file for all your tests. ClassyClass Citizen I also created a repository where I stored my test examples. Check it out and feel free to clone it! Basic Test Structure In this example, we want to test the method of the class. sum() Citizen , @name = name @job = job output = num1 + num2 output RSpec.describe Citizen describe let( ) { Citizen.new( , ) } it expect(accountant.sum( , )).to eq( ) #========== Class ========== class Citizen attr_accessor :name :job def initialize (name, job) end def sum (num1, num2) end end #========== Test ========== do '#sum' do :accountant 'James' 'Accountant' 'returns the sum of two numbers' do 1 2 3 end end end The first step on your test is to create an instance of the object you want to test. After that, you create a using the method. A mock is the requirement for your test to succeed or fail. Mock expect() In this case, we mocked the behavior of the method when it receives and as inputs and expected it to return a value equals to , represented by the matcher . account.sum() 1 2 3 eq(3) RSpec provides a wide variety of matchers, and even the possibility to create custom ones. Check the to find the best for what you want to test. full list of matchers Testing Console Output RSpec lets you test the messages your program will output to the console. The Mocking process is similar, but with a few more elements attached to the method. Check below. expect() puts RSpec.describe ClassyClass let( ) { described_class.new } describe it expect { stacey_instance.exists? }.to output( ).to_stdout #========== Class ========== class ClassyClass def exists? 'Yes' end end #========== Test =========== do :stacey_instance '#exists?' do 'outputs a confirmation that it exists' do "Yes\n" end end end On , we defined the function which outputs on the console when called. A simple case for a simple test. ClassyClass exists? Yes The procedure is the same as before, but here we need to use the matcher to write what the expected output would be, and we need to specify that the content will show up in the console. output() .to_stdout On our test object, we used the built-in method to create a new instance of . This is a feature offered by RSpec, but if you prefer, you can always use to achieve the same effect. described_class.new ClassyClass ClassyClass.new Since we are testing the method, we need to include in our expectation, which represents the line-break on Ruby. We could take out if we were testing the method. puts \n \n print We can also test multi-line outputs using the same configuration. We just need to add between each line and we're set! \ , @name = name @job = job puts RSpec.describe ClassyClass let( ) { described_class.new } let( ) { Citizen.new( , ) } let( ) { double( ) } describe it allow(double_trouble).to receive( ).and_return( ) allow(double_trouble).to receive( ).and_return( ) expect { stacey_instance.check_citizen(double_trouble) }.to output( ).to_stdout it expect { stacey_instance.check_citizen(real_deal) }.to output( ).to_stdout it expect(double_trouble).to receive( ).and_return( ) expect(double_trouble).to receive( ).and_return( ) expect { stacey_instance.check_citizen(double_trouble) }.to output( ).to_stdout #========== Class ========== class Citizen attr_accessor :name :job def initialize (name, job) end end class ClassyClass def check_citizen (citizen_instance) "This is and he works as " #{citizen_instance.name} #{citizen_instance.job} end end #========== Test =========== do :stacey_instance :real_deal 'John' 'Software Developer' :double_trouble 'citizen' '#check_citizen' do "tells double_trouble's specific name and job" do :name 'John' :job 'Software Developer' "This is John and he works as Software Developer\n" end "tells real_deal's specific name and job" do "This is John and he works as Software Developer\n" end "tells double_trouble's specific name and job using expect" do :name 'John' :job 'Software Developer' "This is John and he works as Software Developer\n" end end end Doubles, Stubs and Mocks Disclaimer: the difference between these terms is very subtle, and in my researches, I noticed that there is overlap between them. I will write down my impressions and what made sense for me when it comes to understand them, but remember that I'm a student, and I don't have the authority or capacity to strictly define those terms yet. For better understanding, please check the references I left at the end of this article. As mentioned before, doubles are generic objects. They do nothing on their own, and you need to assign responses to them through stubs or mocks. They are useful when you're still not certain about the object's class. The most common definition I found for stubs is that they are objects with canned responses. I prefer to think of the stub as the canned response, and the objects that are modified to be called stubbed objects. You can stub an object using the method. allow() , @name = name @job = job puts RSpec.describe ClassyClass let( ) { described_class.new } let( ) { Citizen.new( , ) } let( ) { double( ) } describe it allow(double_trouble).to receive( ).and_return( ) allow(double_trouble).to receive( ).and_return( ) expect { stacey_instance.check_citizen(double_trouble) }.to output( ).to_stdout it expect { stacey_instance.check_citizen(real_deal) }.to output( ).to_stdout #========== Class ========== class Citizen attr_accessor :name :job def initialize (name, job) end end class ClassyClass def check_citizen (citizen_instance) "This is and he works as " #{citizen_instance.name} #{citizen_instance.job} end end #========== Test =========== do :stacey_instance :real_deal 'John' 'Software Developer' :double_trouble 'citizen' '#check_citizen' do "tells double_trouble's specific name and job" do :name 'John' :job 'Software Developer' "This is John and he works as Software Developer\n" end "tells real_deal's specific name and job" do "This is John and he works as Software Developer\n" end end end In the first test, you can see the method in action. First, you specify which variable you want to stub ( in this case). Then you use the method to tell which method should be available for that object, and to determine how it will respond when the method is called. allow() double_trouble receive() and_return() In short, we can say that allow(double_trouble).to receive(:name).and_return ('John') is a way to guarantee that double_trouble will behave as . double_trouble.name = 'John' Remember that isn't an instance of . Stubbing or mocking lets us give any behavior we want to any object, which is useful for cases where we don't have a class specified, but we know how a method should treat an object. We gear a double object so it acts as a real one. double_trouble Citizen The second test confirms that. The object is an instance of with the same attributes of the stubbed object . The output is the same for both scenarios and the tests pass. real_deal Citizen double_trouble We can also have a more generalist approach, not giving any specific value for and . .name .job , @name = name @job = job puts RSpec.describe ClassyClass let( ) { described_class.new } let( ) { double( ) } describe it allow(double_trouble).to receive( ) allow(double_trouble).to receive( ) expect { stacey_instance.check_citizen(double_trouble) }.to output( ).to_stdout #========== Class ========== class Citizen attr_accessor :name :job def initialize (name, job) end end class ClassyClass def check_citizen (citizen_instance) "This is and he works as " #{citizen_instance.name} #{citizen_instance.job} end end #========== Test =========== do :stacey_instance :double_trouble 'citizen' '#check_citizen' do "tells double_trouble's generic name and job" do :name :job "This is and he works as \n" #{double_trouble.name} #{double_trouble.job} end end end We just stubbed the ability to respond to calls for and , and in the output, we expect it to call for and . .name .job #{double_trouble.name} #{double_trouble.job} This implies that it doesn't matter what are the values of those two attributes. The method will figure out how to handle them and will produce the output accordingly. check_citizen() That's enough of stubbing, now let's talk about Mocking. , @name = name @job = job puts RSpec.describe ClassyClass let( ) { described_class.new } let( ) { double( ) } describe it expect(double_trouble).to receive( ).and_return( ) expect(double_trouble).to receive( ).and_return( ) expect { stacey_instance.check_citizen(double_trouble) }.to output( ).to_stdout #========== Class ========== class Citizen attr_accessor :name :job def initialize (name, job) end end class ClassyClass def check_citizen (citizen_instance) "This is and he works as " #{citizen_instance.name} #{citizen_instance.job} end end #========== Test =========== do :stacey_instance :double_trouble 'citizen' '#check_citizen' do "tells double_trouble's specific name and job using expect" do :name 'John' :job 'Software Developer' "This is John and he works as Software Developer\n" end end end The test using mocking is pretty much the same as using stubbing, with the difference of using instead of . That's possible because, for each scenario, only the last is treated as an uncertain expectation. The others are treated as expectations taken for granted. expect() allow() expect() Conceptually, they also aren't the same. When you stub an object, you are giving actions to them, while in the case of mocking, you assume that they already can perform those actions. This is a very subtle difference, and in my experience as a beginner, I still didn't find a case where there's a practical difference in the results of these techniques. Maybe on more complex scenarios, it's useful to apply one or another. For now, I prefer to use because it's easier to catch at a glance which lines are giving actions and which one is the expectation. allow() User Input To emulate user input isn't hard, but can trick a beginner into spend hours of research, especially if he/she is stubborn like me and wants the most vanilla solution possible. Well, the solution I came up is pretty vanilla (which means you don't need to install additional gems), but it does need us to require a class that isn't loaded by default on the ruby sessions. So bare with me and don't mind this "transgression" of the vanilla ways, it pays off when you realize that you'll have another tool at your disposal. The class I'm talking about is called . It creates objects that store one or more strings. When associated with the variable , this object will provide its stored content whenever the code calls for user input. StringIO $stdin Let's play a little with this object on IRB, so you get used to it! Call on your terminal and follow the instructions bellow. irb io = StringIO.new( ) io.puts $stdin = io gets gets gets io.rewind gets $stdin = STDIN require 'stringio' 'Hi!' # The string is optional. You can create an empty StringIO # if you want. 'Hello!' # This is another way of adding strings to the object # Plug the object into $stdin so it uses io inputs instead of asking # the user for it # Call gets two times, and you'll receive 'Hi!' and 'Hello!', from # the third time on, you'll get nil. # Sets the pointer of the object to the first string # Calling gets returns 'Hi!' again! # After experimenting, set $stdin back to its original. If you feel curious about , you can , but basically, it's a constant that "listens" to the user input and pass it to the ruby program through the variable. STDIN check this article $stdin Now that you have a good grasp on objects, let's proceed to the test. StringIO , @name = name @job = job puts citizen_instance.name = gets.chomp RSpec.describe ClassyClass let( ) { described_class.new } describe let( ) { StringIO.new( ) } let( ) { Citizen.new( , ) } it $stdin = input expect { stacey_instance.change_name(new_deal) }.to output( ).to_stdout. change { new_deal.name }.to( ) $stdin = STDIN #========== Class ========== class Citizen attr_accessor :name :job def initialize (name, job) end end class ClassyClass def change_name (citizen_instance) "What's the new name of the citizen?" end end #========== Test =========== require 'stringio' do :stacey_instance '#change_name' do :input 'Larry' :new_deal 'Mark' 'Banker' "receive user input and change citizen's name" do "What's the new name of the citizen?\n" and 'Larry' end end end My goal is to test the ability of the method to change the attribute of a object using a string provided by the user. change_name() name Citizen The first step is to create the object with the string you want to pass as user input and plug it on . Next, you define your expectations with the method. StringIO $stdin expect() At first, I created this test with only one expectation, but the question always showed up in the RSpec output. The solution for this was to create another expectation that tests the output generated by the method. change_name() The second expectation was added through the method. Then, I used to test if is capable of changing the attribute of the object to the string provided by . .and change() change_name() name new_deal input After that, I restored the to its default configuration. The test works! This is how you test user input! $stdin Conclusion RSpec provides way more possibilities than what I showed in this article, but I'm confident that following the examples I gave and having all these concepts in mind, a beginner can start writing tests right from the bet. References , by Jon Rowe Composing Matchers , by Jon Rowe Compound Expectations , by The Odin Project Introduction to RSpec , by Joël Quenneville IO in Ruby , by Jon Rowe Let and let! , by Derek Dyer Ruby | Rspec | Mocks & Stubs , by RubyGuides StringIO in Ruby: How it Works & How to Use it , by Dhairya Punjabi Testing codes that interact with standard input and output in Ruby (using RSpec)