Что эквивалентно database_cleaner в Rails 6? - PullRequest
0 голосов
/ 14 марта 2020

У меня есть спец. c, с объектом и двумя контекстами. В одном контексте я установил один ключ на ноль, а в другом нет:

describe SomeClass::SomeService, type: :model do
  describe '#some_method' do

    subject { described_class.new(params, current_user).some_method }
    mocked_params = {
      min_price: 0,
      max_price: 100
    }

    let(:params) { mocked_params }
    let(:current_user) { User.create(email: 'name@mail.com') }

    context 'with invalid params' do
      it 'returns nil if any param is nil' do
        params[:min_price] = nil
        expect(subject).to eq(nil)
      end
    end

    context 'with valid params' do
      it 'returns filtered objects' do
        expect(subject).to eq([])
      end
    end
  end
end

Проблема в том, что второй тест не пройден, потому что min_price все еще равен нулю.

  1. Я читал, что из Rails 5 и дальше мне не нужно database_cleaner. Нужно это или нет?
  2. Я думал, что метод let создает новый объект каждый раз, когда видит переменную. Поскольку у меня есть два контекста, и метод subject вызывается в обоих из них, а внутри subject у меня есть переменная params, почему объект params не является новым со всеми полями в каждом контекст?

Ответы [ 2 ]

3 голосов
/ 14 марта 2020

Я читал, что с 5-го рельсов мне не нужен database_cleaner. Нужно или нет?

Нет. Это больше не нужно. В предыдущих версиях Rails метод отката транзакций базы данных работал (иногда) только с TestUnit / Minitest и фикстурами.

Я думал, что метод let создает новый объект каждый раз, когда видит переменная. Поскольку у меня есть два контекста, и метод субъекта вызывается в обоих из них, а внутри субъекта у меня есть переменная params, почему объект params не является новым в каждом контексте? (со всеми полями)

Это совершенно неверно.

Используйте let для определения запомнившегося вспомогательного метода. Значение будет кэшировано для нескольких вызовов в одном и том же примере, но не во всех.

Когда вы выполните:

mocked_params = {
  min_price: 0,
  max_price: 100
}

let(:params) { mocked_params }

Вы действительно просто возвращаете ссылку на объект mocked_params, а затем изменив этот объект.

Если вы сделаете:

let(:params) do 
 {
    min_price: 0,
    max_price: 100
  }
end

При первом вызове let вы получите новый объект ha sh, и значение будет затем кэшироваться, но не делиться между примерами. Но это действительно верхушка айсберга с этой характеристикой c.

describe SomeClass::SomeService, type: :model do
  describe '#some_method' do
    let(:current_user) { User.create(email: 'name@mail.com') }
    # explicit use of subject is a serious code smell!
    let(:service) { described_class.new(params, current_user) }

    context 'with invalid params' do
      # since let is lazy loading we can define it in this context instead
      let(:params) do
        {
          min_price: nil,
          max_price: 100
        }
      end

      it 'returns nil if any param is nil' do
        # actually call the method under test instead of misusing subject
        # this makes it much clearer to readers what you are actually testing
        expect(service.some_method).to eq(nil)
      end
    end

    context 'with valid params' do
      let(:params) do
        {
          min_price: 0,
          max_price: 100
        }
      end

      it 'returns filtered objects' do
        expect(service.some_method).to eq([])
      end
    end
  end
end

Также весьма сомнительно, почему тестируемый объект принимает ha sh в качестве первого позиционного параметра, а не в качестве последнего параметра который является Ruby способом.

2 голосов
/ 14 марта 2020

Это происходит потому, что вы инициализируете mocked_params только один раз при загрузке файла, а затем изменяете это значение ha sh в первом тесте.

Вместо этого создайте параметры в блоке let, который приведет к воссозданию ха sh для каждого теста.

Изменить

mocked_params = {
  min_price: 0,
  max_price: 100
}

let(:params) { mocked_params }

на

let(:params) do
  {
    min_price: 0,
    max_price: 100
  }
end
...