2016-02-18 2 views
1

Rails 4.2 тестирования с MiniTest и моккогася объекты, извлеченных из базы данных

Я хочу, чтобы мое модульное тестирование в рамках модели этого тестирование - не тестирование функциональности сделали другую модель. Модель has_many :language_progresses и я пытаюсь проверить метод, который принимает подмножество тех language_progresses и суммирует выход метод, называемый на них, как это:

def outcome_month_score(outcome_area, year = Date.today.year, month = Date.today.month) 
    lps = language_progresses.includes(:progress_marker). 
    where("progress_markers.topic_id" => outcome_area.id) 
    lps.inject(0){ |sum, lp| sum + lp.month_score(year, month) } 
end 

вот мой тест:

it "must calculate the outcome score for an outcome area in a month" do 
    year = 2015 
    month = 1 
    pm1 = progress_markers(:skills_used) 
    pm2 = progress_markers(:new_initiatives) 
    pm_other = progress_markers(:devotional) 
    lp1 = LanguageProgress.new progress_marker: pm1 
    lp2 = LanguageProgress.new progress_marker: pm2 
    lp_other = LanguageProgress.new progress_marker: pm_other 
    lp1.stubs(:month_score).with(year, month).returns(1) 
    lp2.stubs(:month_score).with(year, month).returns(2) 
    lp_other.stubs(:month_score).with(year, month).returns(4) 
    state_language.language_progresses << [lp1, lp2, lp_other] 
    state_language.save 
    _(state_language.outcome_month_score(topics(:social_development), year, month)).must_equal 3 
end 

Моя проблема в том, что окурки перестают работать до того, как их результаты суммируются в методе. Они все возвращают 0, хотя я проверил, что они отлично работают в тесте сразу после их создания.

Я думаю, что когда языковые_программы загружаются из db, заглушки отменены.

Как проверить этот метод?

ответ

2

Ваша догадка правильная - использование интерфейса запросов ActiveRecord создает экземпляры экземпляра LanguageProgress и игнорирует ваш заглушку в отношении.

В общем, для такого метода, который использует нетривиальное выражение запроса, я бы не стал тестировать его изолированно. Этот метод, скорее всего, сломается, изменив SQL-запрос на что-то недействительное, и любой уровень модульного тестирования, который вы пишете, должен был бы заглушить методы запросов, чтобы вы потеряли гарантию, что выполняемый запрос действительно является SQL.

В вашем случае, возможно, будет сложно создать полностью действующие языковые прогрессии и уметь рассуждать о своих месячных оценках. Я предлагаю вам выделить две части метода: поиск, который включает SQL, и действительно не может быть по-настоящему модульным тестированием и вычислением, которое легко провести с помощью модульного тестирования.

def progresses_for_area(outcome_area) 
    language_progresses.includes(:progress_marker). 
    where("progress_markers.topic_id" => outcome_area.id) 
end 

def outcome_month_score(outcome_area, year = Date.today.year, month = Date.today.month) 
    progresses_for_area(outcome_area).inject(0){ |sum, lp| sum + lp.month_score(year, month) } 
    # or: 
    # progresses_for_area(outcome_area).map { |lp| lp.month_score(year, month) }.sum 
end 

Тогда ваш тест будет:

it "must calculate the outcome score for an outcome area in a month" do 
    year = 2015 
    month = 1 

    pm1 = progress_markers(:skills_used) 
    pm2 = progress_markers(:new_initiatives) 
    lp1 = LanguageProgress.new progress_marker: pm1 
    lp2 = LanguageProgress.new progress_marker: pm2 
    lp1.stubs(:month_score).with(year, month).returns(1) 
    lp2.stubs(:month_score).with(year, month).returns(2) 

    state_language.stubs(:progresses_for_area).returns([lp1, lp2]) 

    _(state_language.outcome_month_score(topics(:social_development), year, month)).must_equal 3 
end 

И вы могли бы написать отдельный тест, чтобы убедиться, что progresses_for_area работы, которые были бы проще, так как вам не нужно вычислять никаких month_scores.

+0

Хороший ответ, и я, вероятно, сделаю его принятым, если бы In недавно не прочитал статью, критикующую тестовую версию, заявив, как мы добавляем ненужную сложность в код, чтобы упростить тестирование. Я не против TDD, но я думаю, что есть что-то в том, что он говорит, и этот ответ может быть примером этого. –

+2

Я бы сказал, что мой рефакторинг вашей модели на самом деле является лучшим кодом, и улучшенная тестируемость - лишь симптом этого. Между вашим ответом и моей, я знаю, какую модель и тест я бы предпочел сохранить ... –

0

Я нашел способ сделать это, чтобы заглушить два метода в коллекции state_languages.language_progresses. Этими двумя методами являются includes и where. Это приведет к тому, что ActiveRecord не сможет извлечь объекты LanguageProgress из базы данных. Таким образом:

it "must calculate the outcome score for an outcome area in a month" do 
    year = 2015 
    month = 1 
    pm1 = progress_markers(:skills_used) 
    pm2 = progress_markers(:new_initiatives) 
    pm_other = progress_markers(:devotional) 
    lp1 = LanguageProgress.new progress_marker: pm1 
    lp2 = LanguageProgress.new progress_marker: pm2 
    lp_other = LanguageProgress.new progress_marker: pm_other 
    lp1.stubs(:month_score).with(year, month).returns(1) 
    lp2.stubs(:month_score).with(year, month).returns(2) 
    lp_other.stubs(:month_score).with(year, month).returns(4) 
    state_language.language_progresses << [lp1, lp2, lp_other] 
    state_language.language_progresses.stubs(:includes).returns state_language.language_progresses 
    state_language.language_progresses. 
    stubs(:where). 
    with("progress_markers.topic_id" => topics(:social_development).id). 
    returns state_language.language_progresses.select{ |lp| 
     lp.progress_marker.topic.id == topics(:social_development).id 
    } 
    _(state_language.outcome_month_score(topics(:social_development), year, month)).must_equal 3 
end 

здесь я сделал метод includes просто вернуть language_progresses коллекцию и метод where возвращает подмножество этой коллекции.

Недостатком этого является то, что мы не проверяем, что запрос в методе, который мы тестируем, возвращает правильные записи - если бы тест не прошел, но реальный метод дал бы неправильный вывод , Вот почему я также указал параметр для заглушки where. Таким образом, я могу быть уверенным, что с этим параметром выбор из LanguageProgress es, который я делаю в тесте, соответствует таковому в реальном методе, создаваемом где. Если параметр изменяется в тестируемом методе, тогда тест прекратит передачу. Это не безупречно, но я думаю, что это достаточно близко.

Смежные вопросы