1

I'm having this class method on my Post model for getting archives

def self.archives
    Post.unscoped.select("YEAR(created_at) AS year, MONTHNAME(created_at) AS month, COUNT(id) AS total")
        .group("year, month, MONTH(created_at)")
        .order("year DESC, MONTH(created_at) DESC")
end

This is the test I have wrote for my method

context '.archives' do

  first = FactoryGirl.create(:post, published_at: Time.zone.now)
  second = FactoryGirl.create(:post, published_at: 1.month.ago)

  it 'returns articles archived' do
    archives = Post.archives()

    expect(
      [{
        year: first.published_at.strftime("%Y"),
        month: first.published_at.strftime("%B"),
        published: 1
      },
      {
        year: second.published_at.strftime("%Y"),
        month: second.published_at.strftime("%B"),
        published: 1
      }]
      ).to match_array(archives)
  end
end

However I get the following error

expected collection contained:  [#<Post id: nil>, #<Post id: nil>]
actual collection contained:    [{:year=>"2017", :month=>"October", :published=>1}, {:year=>"2017", :month=>"September", :total=>1}]
the missing elements were:      [#<Post id: nil>, #<Post id: nil>]
the extra elements were:        [{:year=>"2017", :month=>"October", :total=>1}, {:year=>"2017", :month=>"September", :total=>1}]

So although I have created 2 factories, the archives array is empty. What am I doing wrong?

0

2 Answers 2

1

Rspec standard is to use the let syntax for defining variables within a context or describe block. The test should look something like this:

describe '.archives' do
  let!(:first) { FactoryGirl.create(:post, published_at: Time.zone.now) }
  let!(:second) { FactoryGirl.create(:post, published_at: 1.month.ago) }

  it 'returns year, month, and total for articles archived' do
    actual_attributes = Post.archives.map { |post| [post.year, post.month, post.total] }
    expected_total = 1 # I don't know why the query is returning 1 for total, but including this for completeness
    expected_attributes = [first, second].map { |post| [post.created_at.year, post.created_at.strftime("%B"), expected_total] }

    expect(actual_attributes).to match_array(expected_attributes)
  end
end

The issue here is that you are comparing records pulled with only a few attributes (the result of your SQL query) with fully-formed records (created by your test). This test pulls the applicable attributes from both groups and compares them.

Sign up to request clarification or add additional context in comments.

7 Comments

expected collection contained: [#<Post id: nil>, #<Post id: nil>]actual collection contained: [#<Post id: 1, user_id: 4, title: "This is the Post title", excerpt: "This is the Post body", created_at: "2017-10-04 21:25:31", updated_at: "2017-10-04 21:25:31">]
I've tweaked it a bit. Please try again.
I tried it my self to reverse the arrays but nothing changes
Did you add the ! after let?
Try adding the additional expect shown in the edited answer, and tell me if it fails on the first expect or the second.
|
1

Actual array is not empty, it's an array of two Post instances with ids unset (because Select in .archives method doesn't contain id field). You could compare expected hashes not with archives, but with smth like that:

actual = Post.archives().map do |post| 
  { year: post["year"].to_s, month: post["month"], published: post["total"] }
end

expected = [{
  year: first.published_at.strftime("%Y").to_s,
  month: first.published_at.strftime("%B"),
  published: 1
},
{
  year: second.published_at.strftime("%Y").to_s,
  month: second.published_at.strftime("%B"),
  published: 1
}]

expect(actual).to match_array(expected)

6 Comments

Not sure I understand, you mean this should be in place of my expect?
Clarified the answer.
expected collection contained: [{:year=>"2017", :month=>"October", :published=>1}, {:year=>"2017", :month=>"September", :published=>1}]actual collection contained: []the missing elements were: [{:year=>"2017", :month=>"October", :published=>1}, {:year=>"2017", :month=>"September", :published=>1}]
with let! I get actual collection contained: [{:year=>2017, :month=>"October", :published=>1}, {:year=>2017, :month=>"September", :published=>1}] the missing elements were: [{:year=>"2017", :month=>"October", :published=>1}, {:year=>"2017", :month=>"September", :published=>1}] the extra elements were: [{:year=>2017, :month=>"October", :published=>1}, {:year=>2017, :month=>"September", :published=>1}]
Actual collection contains year as an integer, you should convert it to string
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.