In order to test our backend logic, we will be adding some new concepts to our tests. These concepts aren't just relevant now — we'll also use them in the next section when we start working with databases. Let's go over these concepts before we delve more deeply into our backend logic. Note: You don't need to add any of this code to your application yet as we are still covering key concepts. We will add all necessary methods to our code soon.
We'll name our class and instance methods slightly differently in our tests:
describe '#Album' do
describe('.all') do
# Note that the class method all() is preceded by a .
end
describe('#save') do
# Note that the instance method save() is preceded by a #.
end
end
...
When testing Ruby code, class methods are preceded by a .
and instance methods begin with #
. This makes it easier for other developers (and us) to quickly take a look at our code and see what we're testing.
Sometimes we'll need to check and see if two objects are "equal" to each other, especially when we are writing tests. For instance, if two albums have the same name, artist, year, and so on then they should be the same album. However, it's not quite that simple. Let's go into IRB and add the following code:
class Album
def initialize(name)
@name = name
end
end
This just allows us to create Album
s with a name. Now let's compare two Album
s that have the same name:
> album = Album.new("Blue")
=> #<Album:0x007f9b9132ed38 @name="Blue">
> irb(main):007:0> album2 = Album.new("Blue")
=> #<Album:0x007f9b91314208 @name="Blue">
> irb(main):008:0> album == album2
=> false
Because they are different objects, Ruby does not recognize that they are supposed to be the same albums. In order to fix this for our tests, we need to override the equality operator ==
. We should always be very careful when overriding Ruby's built-in methods; it's usually not a good idea because it can lead to unexpected behavior, but in this case, we genuinely need it for our tests.
Here's a method that fixes this issue for RSpec:
class Album
attr_reader :name
...
def ==(other_album)
self.name.eql?(other_album.name)
end
end
Here we use the eql?
method to check if both Album
s have the same name. If they do, the ==
operator will return true
. Try it out in IRB. This method needs to be modified and expanded for each new attribute. For instance, if an Album
has a name, artist, and year, our method would look like this:
def ==(other_album)
self.name.eql?(other_album.name) && self.artist.eql?(other_album.artist) && self.year.eql?(other_album.year)
end
For the basic application we build in this section, we'll keep it very simple and Album
s will only have a name attribute. If you end up building out the application further (and the same applies for any other application you work on), you'll have to modify this method as you add more attributes to the class.
If our application had a test database, we'd want to clear it out between each test. Even though we don't have a test database yet, class variables do have persistence. This means they might change from test to test unless we clear their values. This could cause either false positives or negatives in our tests.
RSpec offers before
and after
blocks to clear or otherwise prepare the test environment between tests. We've already learned about using these kinds of blocks to set up and clean up tests with Jest. The syntax in RSpec looks like this:
before(:each) do
# Code to set up environment between tests goes here.
# We will create an Album.clear() method in the next lesson
# to empty our mock database before each test.
end
Before and after blocks are very common and we will use them regularly.
We can also use instance variables in our before(:each)
blocks like this:
before(:each) do
@album = Album.new( #create a new album )
end
This instance variable will be available to all of our tests. This can DRY up tests considerably, particularly if we are creating the same object in multiple tests.
If any of the concepts covered in this lesson are confusing, don't worry. We'll be incorporating these concepts in our code. You can always use this lesson as a reference as needed.
We're now ready to write and test our backend logic!
We name class and instance methods slightly differently in our tests:
describe '#Album' do
describe('.all') do
# Note that the class method all() is preceded by a .
end
describe('#save') do
# Note that the instance method save() is preceded by a #.
end
end
...
A sample method for overriding the equality operator for tests. This method should always compare all properties.
class Album
attr_reader :name
...
def ==(other_album)
self.name.eql?(other_album.name)
end
end
before(:each) do
# Code to set up environment between tests goes here. We will create an Album.clear() method in the next lesson to empty our mock database before each test.
end
We can also use instance variables in our before(:each)
blocks like this:
before(:each) do
@album = Album.new( #create a new album )
end
Lesson 10 of 37
Last updated August 7, 2022