2017-02-06 4 views
3

Мое понимание заключается в том, что модульное тестирование должно тестировать классы изолированно, фокусируясь на гранулированном поведении и заменяя объекты других классов, используя, по возможности, парные/макеты. (Пожалуйста, поправьте меня, если я ошибаюсь здесь.)Как тестировать класс, который сильно зависит от других классов?

Я пишу драгоценный камень с классом под названием MatchList. MatchList::new принимает два аргумента, каждый экземпляр другого класса под названием MatchPhrase. MatchPhrase содержит некоторое поведение, которое MatchList сильно зависит от (, т. Е., если вы кормите ничего, кроме MatchPhrase, до MatchList::new, вы получите кучу ошибок «неопределенного метода»).

Моя текущая (? Наивная) испытательная установка использует let заявления для назначения переменных для использования в моих примерах:

let(:query)  { MatchPhrase.new('Good Eats') } 
let(:candidate) { MatchPhrase.new('Good Grief') } 
let(:match_list) { MatchList.new(query, candidate) } 

Как я пишу это модульное тестирование? Правильно ли я думаю, что это нужно сделать, не вызывая класс MatchPhrase? Возможно ли это?


Для справки, вот что MatchList класса выглядит следующим образом:

class MatchList < Array 
    attr_reader :query, :this_phrase, :that_phrase 

    def initialize(query, candidate) 
    super(query.length) 
    @query = query 
    @this_phrase = query.dup 
    @that_phrase = candidate 
    find_matches until none?(&:nil?) 
    end 

    private 

    def find_matches 
    query.each.with_index do |this_token, i| 
     next unless self[i].nil? 
     that_token = this_token.best_match_in(that_phrase) 
     next if that_token.match?(that_token) && 
       this_token != that_token.best_match_in(this_phrase) 
     self[i] = this_token.match?(that_token) ? that_token : NilToken.new 
     this_phrase.delete_once(this_token) 
     that_phrase.delete_once(that_token) 
    end 
    end 
end 
+0

Интересный вопрос. Когда это невозможно или будет слишком много работать для тестирования класса класса 'A', я обычно тестирую' A & B', 'A & C' и' A & D'. Когда все они терпят неудачу, есть сильное подозрение, что «А» является виновником.Посмотрите, что советует сообщество. –

+0

Вы на правильном пути. Вы должны подставлять объекты других классов с помощью double/mocks, где это возможно. Это одно из преимуществ DI. – Surya

ответ

1

Mocking должно быть сделано на законных основаниях, а не в принципе.

Если есть только один класс соавторов, и ваш основной класс сильно связан с ним, издевательство над соавтором принципиально может привести к большей хрупкости, чем пользе, поскольку макет не будет отражать поведение соавтора.

Mocks and stub - хорошие кандидаты, когда вы можете рассуждать против интерфейса mock вместо реализации. Давайте игнорировать существующий код и посмотреть на интерфейсах используются здесь:

  • MatchList.new занимает query и candidate
  • query является Перечислимым, содержащими объектами, которые реализуют best_match_in?(something)
  • Объекты query также реализуют delete_once(something)
  • candidate также осуществляет delete_once(something)
  • best_match_in? r eturns то, что реализует match? и best_match_in?

Глядя на интерфейсах в использовании, MatchList кажется полагаться довольно сильно на реализацию query и candidate объектов. Пахнет как случай feature envy для меня. Возможно, эта функция должна находиться в пределах MatchPhrase.

В этом случае я бы написал модульные тесты, используя фактические объекты MatchPhrase с примечанием, чтобы реорганизовать этот код.

2

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

В моем понимании это неправда. Использование удваивает/mocks имеет преимущества и недостатки.

Преимущество в том, что вы можете использовать медленную службу, такую ​​как база данных, электронную почту и издеваться над ней быстродействующим объектом.

Недостаток - это тот объект, который вы издеваетесь, не является «реальным» объектом и может удивить вас и вести себя иначе, чем реальный объект.

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

Учитывая ваш случай:

let(:query)  { MatchPhrase.new('Good Eats') } 
let(:candidate) { MatchPhrase.new('Good Grief') } 
let(:match_list) { MatchList.new(query, candidate) } 

Там действительно нет преимущества макета запроса или кандидата.

1

Ваше понимание использования тестовой части является правильным. Его сосредоточение на гранулированном поведении. Например, индивидуальные методы.

Однако, чтобы проверить индивидуальные методы, попробуйте использовать double/mocks не целесообразно. Марко ^^ изложил преимущества/недостатки издевательств. Я лично предпочитаю не использовать вдвое больше, чем могу.

Его всегда баланс между скоростью ваших тестов и создаваемыми вами объектами.

Прежде чем перейти к удвоениям/издевательствам, стоит подумать, можете ли вы написать свой тест, не сохраняя значения в БД. Как и раньше. Это быстрее, чем сохранение и извлечение значений из БД.

Еще одна вещь: private методы и, как правило, не протестированы. Это с пониманием, вызывающий ваш частный метод будет иметь единичный тест.

let(:query)  { MatchPhrase.new('Good Eats') } 
let(:candidate) { MatchPhrase.new('Good Grief') } 

it 'check your expectation' do 
    expect(MatchList.new(query, candidate).query).to <check the expectation> 
end 

Однако я буду пересматривать следующие моменты.

1 - сделать и хотите позвонить find_matches из инициализаторе

2 - Пусть find_matches возвращать значение, а затем присвоить его переменной @query (так что его легко проверить метод с возвращаемым значением)

3 - переименовать query параметров в INIT к чему-то еще (как раз, чтобы избежать путаницы)

И золотое правило Если его трудно проверить (специально модульное тестирование), может быть, ваш делают что-то неправильно.

НТН

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