References Everyday Rails-Introduction to Rails Testing with RSpec https://leanpub.com/everydayrailsrspec-jp
An object that pretends to be a real object used for testing. It is also called a test double. The mock does not access the database, which reduces test time.
Overrides the object's methods and returns a predetermined value. A stub is a dummy method that, when called, returns a real result for testing. Stubs are often used to override the default functionality of a method. In particular, processing that uses databases and networks is targeted.
The name (combination of first_name + last_name) method in the User object is delegated to the Note object and attributes are added.
User attributes first_name and last_name ↓ Combine first_name and last_name with User's name method ↓ Add the combined value as user_name to the Note attribute
Test if this is working properly.
First code.rb
it "Delegate getting the name to the user who created the note" do
user = FactoryBot.create(:user, first_name: "Fake", last_name: "User")
note = Note.new(user: user)
expect(note.user_name).to eq "Fake User"
end
In this code User object needs to be saved in DB and made persistent. As the number and complexity increase, all the uninteresting processing swells, even though it's a Note model test. And in the Note model test, I know too much about the implementation of the User model. This test is not interested in the fact that the name in the User model has the attributes first_name and last_name. All you have to do is know that the User model returns the string name.
Based on this, refactoring will be performed.
Refactored code.rb
it "Delegate getting the name to the user who created the note" do
#Replace User object with double(mock)
user = double("user", name: "Fake User")
note = Note.new
#note.A stub that explicitly states that you want to call user
allow(note).to receive(:user).and_return(user)
expect(note.user_name).to eq "Fake User"
end
Replaced the persistence of the User object with a mock. Since we want the mock object to have a method, Key and Value are specified after the second argument. (User's name method returns "Fake User") If you look at this object, you'll see a class named Double. Note that this mock is an object that only knows how to return a name. Of course, if you try to test first_name as shown below, you will get an error because you cannot return a value.
expect(note.user.first_name).to eq "Fake"
Next, let's look at the stub. The stub is created with allow. (Use allow (obj) .to receive (: method) if the receiver object is not a mock object but an existing object) The writing method is shown below.
#and_The return value of the method is specified by return.
allow(obj).to receive(:method).and_return(true)
The refactored code tells me to call note.user somewhere in the test.
allow(note).to receive(:user).and_return(user)
When user.name is actually called, the value of note.user_id is used to search for the appropriate user in the database, and instead of returning the found user, the result is a test double named user.
Click here for a very easy-to-understand article Developers.IO https://dev.classmethod.jp/articles/rspec-recipe/ An introduction to RSpec that can be used, part 3 "How to write a test using a mock that can be understood from zero" https://qiita.com/jnchito/items/640f17e124ab263a54dd
Now you don't have to save the User object in the DB and make it persistent. And for the Note model test, I was able to refactor it with minimal interest in the User model. However, there are still problems.
RSpec's test double does not verify that the object you are trying to substitute has a method that you want to stub. In other words, the method you call on the stub will be undefined, but the test will pass. When using a test double as much as possible, it is desirable to use a test double with a verification function. Therefore, change to a verified double with a verification function.
Code changed to double with verification.rb
it "Delegate getting the name to the user who created the note" do
#instance_Change to double. Change the first letter of user to uppercase
user = instance_double("User", name: "Fake User")
note = Note.new
allow(note).to receive(:user).and_return(user)
expect(note.user_name).to eq "Fake User"
end
It's basically the same as double, except that you get an error when you stub an undefined instance method. Therefore, it is possible to write a test while confirming that the method exists.
Thank you for staying with us until the end. If you have any suggestions or advice, I would appreciate it if you could comment.
Recommended Posts