Проверка записей базы данных с помощью rspec

#rspec

#rspec

Вопрос:

Где я ошибаюсь?

  describe '#update' do
    let(:new_name) { Faker::Lorem.word }
    let(:request) { patch :update, id: parent_folder.id, folder: { name: new_name, parent_id: nil, user_id: user.id } }

    it 'should change the name' do
      expect{ request }.to change(parent_folder.reload,:name).from(parent_folder.name).to(new_name)
    end

    it 'does work gadnammit' do
      ap parent_folder.reload.name #=> e.g. aqua
      request
      ap parent_folder.reload.name #=> e.g. hortis
    end
  end
  

Результатом ‘следует изменить имя’ является

 Failure/Error: expect{ request }.to change(parent_folder.reload,:name).from(parent_folder.name).to(new_name)# @new_name
expected #name to have changed from "maxime" to "jimmies", but did not change
  

И все же ‘does work gadnammit’ регистрирует два разных имени:

 All examples were filtered out; ignoring {:focus=>true}
F."omnis"
"jimmies"

Failures:

1) FoldersController#update should change the name
     Failure/Error: expect{ request }.to change(parent_folder.reload,:name).from(parent_folder.name).to(new_name)# @new_name
       expected #name to have changed from "maxime" to "jimmies", but did not change
     # ./spec/controllers/folders_controller_spec.rb:62:in `block (3 levels) in <top (required)>'
  

Кроме того, когда вы используете помощник let с Faker, я знаю, что вы получаете новый результат каждый раз, когда вызывается создаваемая им переменная, что может создавать иллюзию, что имя меняется в спецификации ‘does work gadnammit’. Однако изменение его на жестко закодированную строку не дает никакой разницы:

 let(:new_name) { "stagnant" }
  

Кроме того, эта спецификация:

 it 'should make database queries' do
  expect{ request }.to make_database_queries 
end
  

проходит, поэтому я действительно думаю, что это работает…

 SELECT  "users".* FROM "users"  WHERE "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
SELECT  "folders".* FROM "folders"  WHERE "folders"."user_id" = ? AND "folders"."id" = ? LIMIT 1
SAVEPOINT active_record_1
UPDATE "folders" SET "name" = ?, "updated_at" = ? WHERE "folders"."id" = 1
RELEASE SAVEPOINT active_record_1
  

(сопоставитель make_database_queries любезно предоставлен db-query-matchers gem)

TL;DR Почему нет

 expect{ request }.to change(parent_folder.reload,:name).from(parent_folder.name).to(new_name)
  

работает?

Ответ №1:

Параметр parent.folder.rename вычисляется во время change вызова. Он не подвергается (и не может) повторной оценке после выполнения тестируемого блока. Это, скорее всего, объясняет, почему name вызванный результирующий объект не изменяется.

Если вы хотите, чтобы выражение было повторно вычислено до и после выполнения тестируемого блока, вам необходимо передать change блок, как в:

 change { parent.folder.reload }.from(...).to(...)
  

«Параметрическая» версия change работает, когда первый параметр остается неизменным (например, константой), а значение, возвращаемое методом, связанным со вторым параметром, изменяется.

Комментарии:

1. Спасибо, Питер! Жаль, что он не читается почти так же хорошо, как expect{ request }.to change... почему это expect{ delete :destroy, id: child_folder.id }.to change(Folder, :count).by(-1) работает? Конечно, это должно оцениваться Folder.count до и после выполнения запроса, так почему же это не работает при запросе фактической записи?

2. Хорошо, я буду честен, я немного сбит с толку. expect { employee.develop_great_new_social_networking_app }.to change(employee, :title).from("Mail Clerk").to("CEO") из rubydoc.info/github/rspec/rspec-expectations/RSpec / … очень точно соответствует тому, что я хочу, и все же это не работает? Пожалуйста, пример 🙂

3. Мне пришлось задать этот вопрос во второй раз. Не заметил фигурные скобки, игнорируйте меня: P