Project Ramon

A learning journey from a Ruby noob perspective

Code Review: Model specs with Edward Anderson

event_spec_review_header_img

I recently had another code review with Edward Anderson. He wen’t over my model specs, and had several critiques that helped me understand how to write stronger tests. He also brought me more up to speed with how to write specs for my scopes. Scopes can be thought of as very similar to Ruby class methods.

Take a peak at what the Rails guide says about scopes:

Scoping allows you to specify commonly-used queries which can be referenced as method calls on the association objects or models. With these scopes, you can use every method previously covered such as wherejoins and includes. All scope methods will return an ActiveRecord::Relation object which will allow for further methods (such as other scopes) to be called on it.

Below is an example of my event_spec.rb file before my code review:

This looked passable to me before my AirPair with Edward, but he shared a few tips to help me spot the difference.

I learned that there are 3 parts to any spec. He mentioned that the steps aren’t always as strictly delineated as this, but that every spec will contain all 3 of the following steps.

  1. The setup: This can be seen on lines 5, 12, 19, etc… in the first example and where we use FactoryGirl to setup our object.
  2. The execution: This is the expect(argument) portion of lines 6, 8, 13, 15, etc… This is where we call some code to assert our step 3.
  3. Assert our desired result: This looks like the the be_valid on line 6, the be_present on line 8, and so on. When he said assert our desired result, I take it to mean, what we know should make our test pass or fail. The be_valid method passes a .valid? method to our expect argument. On line 6 that would be the argument event, which is the local variable our FactoryGirl object is stored in within that current spec.

Now that we have an idea of the components of a spec file, lets get into how Edward helped me remove, or edit some weak-sauced specs that I wrote.

If you see throughout the entire file in the first example, I have done an ok job of my object setups, which basically says that if my required attributes are nil, then the object, should not save because it is invalid. This is because of the validations, created in the Event.rb model.

Lets have a quick look before moving forward:

On line 6, the validates_presence_of :author, :name, :title, :event_on, :type is what will not allow our object to save, if any of the aforementioned attributes are blank or nil values in our params hash:

We are only checking that the value assigned to event.author on line 4, is present. Edward skillfully explained to me that the method be_present only asserts that expect(event.author).to is not blank? or nil. I’m very glad he broke down the meaning of some Rspec matcher methods, because my thinking before our code review was that if I had a test for an event.author string, and a user entered in a value other than "Jimmy Dean" that, it wouldn’t be_valid because when I ran that through the terminal, it wasn’t passing.

I realized though that when I am assigning the string "Jimmy Dean", that calling be_valid instead of be_present is calling the .valid? method on any value entered by a user. In other words, the client value, should match the value thats actually stored, which is an operation available to me through Ruby’s assignment = operator.. Definitely not something I should be testing for.

Here’s what my final event_spec.rb file looks like after some professional guidance:

Oh, what are those scopes tests at the bottom you ask? It’s time for some scope specs.

Scope Specs

Since I had been digesting Edward’s steps to a spec, I had a lot more confidence in the setup of all of these scope specs. Since we already know that scopes aid us in specifying commonly used queries, and we have an idea of the event.rb data-model, then we can just follow the script. Setup our objects, execute the spec case method we’re testing for, and make an assertion that will provide our desired results.

Taking the first  scope specEvent.upcoming_events, we’ll have a quick walk-thru.

First off, Edward encouraged me to always create groups for similar code behavior. The describe "scopes" do block on line 39 in the above example works well for such a task.

our first spec in our describe ".upcoming_events" block is making assertions that ensure that a query for an upcoming event will only pull records from up to 3 days ahead of the current time of query.

We set up our cases, which is an object stored into future_events and another one stored into past_events. Notice that our future_events has an event_on  2.days.from_now, while our past_events has an event_on 1.day.ago. Can you guess which one shouldn’t be in our upcoming_events query?

So now for us to call out the method that we wrote this spec for. expect(future_events.event_on).to and expect(future_events.event_on).not_to, are establishing the boundaries of the intent of this particular spec so that we should only receive events from up to 3 days in the future.

Our first assertion says that  future_events is expected to be > (be greater than) 1.minute.from_now. If we ever get mixed up on what from_now means relative to the current time. We can just open up IRB or PRY in our terminal and type Time.now a few times.

Here I’ll show what happens when I do it:

event_spec_review_img1

The illustration above is just a visual way of saying that 1.minute.from_now is relative to when the query is executed. Could be right now, or 1 minute from thanksgiving 2020 at midnight, if thats when the query was executed.

There are a host of other things I learned in my last pair with Edward, like some new multi-line comment slight-of-hand tricks, but you should really checkout our pair and consider asking him to share an hour of his time. You’ll most certainly come out of that hour with your brain a little sore, and a huge smile on your face from the educational workout he consistently brings to the table.

Here’s our most recent pair:

Advertisements

Categories: AirPair, Newbie, Ruby on rails

Tags: , , ,

2 replies

Trackbacks

  1. AirPair.com – What I’ve Learned pt. 2 | Project Ramon
  2. Lunch Panoply: Test-Driving a Data-Model | Project Ramon

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s