SlideShare a Scribd company logo
1 of 53
Download to read offline
Антипаттерны
модульного тестирования




 Митин Павел, RubyConfUa 2010
О себе

• Ruby on Rails разработчик
• 4 года практики в стиле test-driven development
• http://novembermeeting.blogspot.com
Indented test code vs Правило Шапокляк

describe PopularityCalculator, '#popular?' do
  it 'should take into account 
      the comment count' do
    subject.popular?(post).should be_false
  end

  # ...
end
Indented test code vs Правило Шапокляк

class PopularityCalculator
  def popular?(post)
  end
end
Indented test code vs Правило Шапокляк

it 'should take into account the comment count' do
  posts = (0..19).map do |n|
    post_with_comment_count n
  end

  posts.each do |post|
    if 10 < post.comment_count
      subject.popular?(post).should be_true
    else
      subject.popular?(post).should be_false
    end
  end
end
Indented test code vs Правило Шапокляк

class PopularityCalculator
  THRESHOLD = 10

  def popular?(post)
    THRESHOLD < post.comment_count
  end
end
Indented test code vs Правило Шапокляк

it 'should take into account the comment count' do
  posts = (0..19).map do |n|
    post_with_comment_count n
  end

  posts.each do |post|
    if 10 < post.comment_count
      subject.popular?(post).should be_true
    else
      subject.popular?(post).should be_false
    end
  end
end
Indented test code vs Правило Шапокляк

Мотивация:
 • борьба с дублированием
 • работы с неконтролируемыми аспектами
   системы(время, дисковое пространство и т.д.)
Indented test code vs Правило Шапокляк




             Правило Шапокляк:
Это хорошо ... хорошо, что Вы зеленый и плоский
Indented test code vs Правило Шапокляк

it "should return true if the comment count /
    is more then the popularity threshold" do

  post = post_with_comment_count THRESHOLD + 1
  subject.popular?(post).should be_true

  post = post_with_comment_count THRESHOLD + 100
  subject.popular?(post).should be_true
end
Indented test code vs Правило Шапокляк

Бенефиты:
 • тесты проще понять
 • тесты содержат меньше ошибок
Production Logic in Test

class PopularityCalculator
  THRESHOLD = 10

  def popular?(post)
    THRESHOLD < post.comment_count
  end
end
Production Logic in Test

it "should take into account the comment count" do
  post = post_with_comment_count 11
  expected = THRESHOLD < post.comment_count
  actual = subject.popular? post
  actual.should == expected
end
Production Logic in Test

it "should take into account the comment count" do
  post = post_with_comment_count 11
  expected = THRESHOLD < post.comment_count
  actual = subject.popular? post
  actual.should == expected
end
Production Logic in Test

Мотивация: упростить получение ожидаемого значения
Production Logic in Test
Production Logic in Test

it "should take into account the comment count" do
  actual = subject.popular? post_with_comment_count(11)
  actual.should be_true
end
Production Logic in Test

Бенефиты: мы действительно тестируем, а не только
улучшаем тестовое покрытие :)
Too Many Expectations

describe NotificationService, "#notify_about" do
  it "should notify the post author by email" do
    notification_service.notify_about @comment
  end

  it "should notify the post author by sms"
end
Too Many Expectations

class NotificationService <
  Struct.new(:email_service, :sms_service)

  def notify_about(comment)
  end
end
Too Many Expectations

before do
  @email_service, @sms_service = mock, mock
  @comment = Comment.new 'some text', 'dummy post'
  @notification_service = NotificationService.
    new @email_service, @sms_service
end
Too Many Expectations

it "should notify the post author by email" do
  @email_service.expects(:deliver_new_comment_email).
    with @comment
  @sms_service.expects :deliver_new_comment_sms
  @notification_service.notify_about @comment
end Too Many Expectations


it "should notify the post author by sms" do
  @email_service.expects :deliver_new_comment_email
  @sms_service.expects(:deliver_new_comment_sms).
    with @comment
  @notification_service.notify_about @comment
end
Too Many Expectations

def notify_about(comment)
  email_service.deliver_new_comment_email comment
  sms_service.deliver_new_comment_sms comment
end
Too Many Expectations

it "should notify the post author by email" do
  @email_service.expects(:deliver_new_comment_email).
    with @comment
  @sms_service.expects :deliver_new_comment_sms
  @notification_service.notify_about @comment
end

it "should notify the post author by sms" do
  @email_service.expects :deliver_new_comment_email
  @sms_service.expects(:deliver_new_comment_sms).
    with @comment
  @notification_service.notify_about @comment
end
Too Many Expectations

def notify_about(comment)
  # email_service.deliver_new_comment_email comment
  sms_service.deliver_new_comment_sms comment
end
Too Many Expectations

1)
Mocha::ExpectationError in 'NotificationService#notify_about should notify the post author by email'
not all expectations were satisfied
unsatisfied expectations:
- expected exactly once, not yet invoked: #<Mock:0xb74cdd64>.deliver_new_comment_email(#<Comment:0xb74cdb70>,
#<Mock:0xb74cdf08>)
satisfied expectations:
- expected exactly once, already invoked once: #<Mock:0xb74cde2c>.get(any_parameters)
- expected exactly once, already invoked once: #<Mock:0xb74cdc9c>.author_id(any_parameters)
- expected exactly once, already invoked once: nil.deliver_new_comment_sms(any_parameters)




2)
Mocha::ExpectationError in 'NotificationService#notify_about should notify the post author by sms'
not all expectations were satisfied
unsatisfied expectations:
- expected exactly once, not yet invoked: #<Mock:0xb74c937c>.deliver_new_comment_email(any_parameters)
satisfied expectations:
- expected exactly once, already invoked once: #<Mock:0xb74c9444>.get(any_parameters)
- expected exactly once, already invoked once: #<Mock:0xb74c92b4>.author_id(any_parameters)
- expected exactly once, already invoked once: nil.deliver_new_comment_sms(#<Comment:0xb74c9188>,
#<Mock:0xb74c9520>)
Too Many Expectations
Too Many Expectations

Причины воспроизведения паттерна: отсутствие
знаний о стаб-объектах
Too Many Expectations

before do
  @sms_service = stub_everything
  @email_service = stub_everything


  @notification_service = NotificationService.
    new @email_service, @sms_service
end
Too Many Expectations

it "should notify the post author by email" do
  @email_service.expects(:deliver_new_comment_email).
    with @comment, @author
 @notification_service.notify_about @comment
end

it "should notify the post author by sms" do
  @sms_service.expects(:deliver_new_comment_sms).
    with @comment, @author
  @notification_service.notify_about @comment
end
Too Many Expectations

Бенефиты: одна ошибка - один падающий тест
Redundant Fixture

describe PostRepository, "#popular" do
  it "should return all popular posts" do
    repository.popular.should include(popular_post)
  end
end
Redundant Fixture

class PostRepository
  def popular
    []
  end
end
Redundant Fixture

it "should return all popular posts" do
  popular_posts = (1..2).map { build_popular_post }
  unpopular_posts = (1..3).
    map { build_unpopular_post }
  posts = (popular_posts + unpopular_posts).shuffle
  repository = PostRepository.new posts

  actual = repository.popular

  actual.should have(2).posts
  actual.should include(popular_posts.first,
                        popular_posts.last)
end
Redundant Fixture

it "should return all popular posts" do
  popular_posts = (1..2).map { build_popular_post }
  unpopular_posts = (1..3).
    map { build_unpopular_post }
  posts = (popular_posts + unpopular_posts).shuffle
  repository = PostRepository.new posts

  actual = repository.popular

  actual.should have(2).posts
  actual.should include(popular_posts.first,
                        popular_posts.last)
end
Redundant Fixture

Мотивация: желание получить в тестовом окружении
“реальные” данные
Redundant Fixture




       VS
Redundant Fixture

before do
  @popular_post = build_popular_post
  @unpopular_post = build_unpopular_post
  @repository = PostRepository.new(
    [@popular_post, @unpopular_post] )
end
it "should return a popular post" do
  @repository.popular.should include(@popular_post)
end
it "shouldn't return an unpopular post" do
  @repository.popular.
    should_not include(@unpopular_post)
end
Redundant Fixture

Бенефиты:
 • простой setup
 • сообщение о падении теста не перегружено лишними
   данными
 • профилактика "медленных" тестов
Neglected Diagnostic vs Ясный красный

describe BullshitProfitCalculator, "#calculate" do
  it "should return the projected profit" do
    actual = subject.calculate 'dummy author'
    actual.should == '$123'.to_money
  end
end

class BullshitProfitCalculator
  def calculate(author)
    '$1'.to_money
  end
end
Neglected Diagnostic vs Ясный красный

'BullshitProfitCalculator#calculate should return the projected
profit' FAILED
expected: #<Money:0xb7447ebc @currency=#<Money::Currency id: usd
priority: 1, iso_code: USD, name: United States Dollar, symbol: $,
subunit: Cent, subunit_to_unit: 100, separator: ., delimiter: ,>,
@cents=12300, @bank=#<Money::VariableExchangeBank:0xb74dabb8
@rates={}, @mutex=#<Mutex:0xb74dab7c>, @rounding_method=nil>>,
    got: #<Money:0xb7448038 @currency=#<Money::Currency id: usd
priority: 1, iso_code: USD, name: United States Dollar, symbol: $,
subunit: Cent, subunit_to_unit: 100, separator: ., delimiter: ,>,
@cents=100, @bank=#<Money::VariableExchangeBank:0xb74dabb8 @rates={},
@mutex=#<Mutex:0xb74dab7c>, @rounding_method=nil>> (using ==)
Neglected Diagnostic vs Ясный красный
Neglected Diagnostic vs Ясный красный

module TestMoneyFormatter
  def inspect
    format
  end
end

class Money
  include TestMoneyFormatter
end
Neglected Diagnostic vs Ясный красный

'BullshitProfitCalculator#calculate should return
the projected profit' FAILED
expected: $123.00,
     got: $1.00 (using ==)
Neglected Diagnostic vs Ясный красный

Было:                 Стало:
красный               красный
                      ясный красный
зеленый               зеленый
рефакторинг           рефакторинг
Neglected Diagnostic vs Ясный красный

Бенефиты: ясное диагностическое сообщение упрощает
дальнейшую поддержку теста
Еще антипаттерны

• глобальные фикстуры
• функциональный код, используемый только в тестах
• нарушение изоляции тестов
• зависимости из других слоев приложения
• тестирование кода фреймворка
Антипаттерны в mocking TDD

• мокание методов тестируемого модуля
• мокание объектов-значений
Еще антипаттерны

• “медленные” тесты
• …
Рекомендуемая литература




• Экстремальное программирование. Разработка
  через тестирование, Кент Бек
• Growing Object-Oriented Software, Guided by Tests
  by Steve Freeman and Nat Pryce
Исходный код примеров

http://github.com/MitinPavel/test_antipatterns.git
Использованные изображения

• http://dreamworlds.ru/kartinki/27030-otricatelnye-
  personazhi-v-multfilmakh.html
• http://www.inquisitr.com/39089/former-police-officer-
  sues-for-discrimination-over-his-alcoholism-disability/
• http://teachpro.ru/source/obz11/Html/der11163.htm
• http://bigpicture.ru/?p=4302
• http://giga.satel.com.ua/index.php?newsid=25654
Спасибо за внимание




 RubyConfUa 2010

More Related Content

What's hot

Small pieces loosely joined
Small pieces loosely joinedSmall pieces loosely joined
Small pieces loosely joinedennui2342
 
RSpec 3: The new, the old, the good
RSpec 3: The new, the old, the goodRSpec 3: The new, the old, the good
RSpec 3: The new, the old, the goodmglrnm
 
oracle soa Examples
oracle soa Examplesoracle soa Examples
oracle soa Examplesxavier john
 
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2RORLAB
 
Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015CiaranMcNulty
 
Getting Started with Maven and Cucumber in Eclipse
Getting Started with Maven and Cucumber in EclipseGetting Started with Maven and Cucumber in Eclipse
Getting Started with Maven and Cucumber in EclipseTom Arend
 
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010singingfish
 
Introduction to Angular js
Introduction to Angular jsIntroduction to Angular js
Introduction to Angular jsMustafa Gamal
 
Finding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobFinding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobCiaranMcNulty
 
Java script – basic auroskills (2)
Java script – basic   auroskills (2)Java script – basic   auroskills (2)
Java script – basic auroskills (2)BoneyGawande
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everythingnoelrap
 
WordPress Hooks Action & Filters
WordPress Hooks Action & FiltersWordPress Hooks Action & Filters
WordPress Hooks Action & FiltersNirav Mehta
 

What's hot (20)

Small pieces loosely joined
Small pieces loosely joinedSmall pieces loosely joined
Small pieces loosely joined
 
RSpec 3: The new, the old, the good
RSpec 3: The new, the old, the goodRSpec 3: The new, the old, the good
RSpec 3: The new, the old, the good
 
oracle soa Examples
oracle soa Examplesoracle soa Examples
oracle soa Examples
 
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
 
Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015
 
Getting Started with Maven and Cucumber in Eclipse
Getting Started with Maven and Cucumber in EclipseGetting Started with Maven and Cucumber in Eclipse
Getting Started with Maven and Cucumber in Eclipse
 
Rspec 101
Rspec 101Rspec 101
Rspec 101
 
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
 
Introduction to Angular js
Introduction to Angular jsIntroduction to Angular js
Introduction to Angular js
 
Finding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobFinding the Right Testing Tool for the Job
Finding the Right Testing Tool for the Job
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
 
Java script – basic auroskills (2)
Java script – basic   auroskills (2)Java script – basic   auroskills (2)
Java script – basic auroskills (2)
 
Frontin like-a-backer
Frontin like-a-backerFrontin like-a-backer
Frontin like-a-backer
 
Actions filters
Actions filtersActions filters
Actions filters
 
Spock
SpockSpock
Spock
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
WordPress Hooks Action & Filters
WordPress Hooks Action & FiltersWordPress Hooks Action & Filters
WordPress Hooks Action & Filters
 
Typescript barcelona
Typescript barcelonaTypescript barcelona
Typescript barcelona
 
Guice gin
Guice ginGuice gin
Guice gin
 
Sapphire Gimlets
Sapphire GimletsSapphire Gimlets
Sapphire Gimlets
 

Viewers also liked

Backbone.js Профилактика сколиоза
Backbone.js Профилактика сколиоза Backbone.js Профилактика сколиоза
Backbone.js Профилактика сколиоза MitinPavel
 
Hypermedia api (HATEOAS)
Hypermedia api (HATEOAS)Hypermedia api (HATEOAS)
Hypermedia api (HATEOAS)MitinPavel
 
Сидристый Станислав: Паттерны и антипаттерны BDD
Сидристый Станислав: Паттерны и антипаттерны BDDСидристый Станислав: Паттерны и антипаттерны BDD
Сидристый Станислав: Паттерны и антипаттерны BDDStanislav Sidristy
 
Command Query Responsibility Segregation and Event Sourcing
Command Query Responsibility Segregation and Event SourcingCommand Query Responsibility Segregation and Event Sourcing
Command Query Responsibility Segregation and Event SourcingMitinPavel
 
Взаимодействие сервисов через Advanced Message
 Queuing Protocol
Взаимодействие сервисов через Advanced Message
 Queuing ProtocolВзаимодействие сервисов через Advanced Message
 Queuing Protocol
Взаимодействие сервисов через Advanced Message
 Queuing ProtocolElena Grahovac
 

Viewers also liked (6)

Backbone.js Профилактика сколиоза
Backbone.js Профилактика сколиоза Backbone.js Профилактика сколиоза
Backbone.js Профилактика сколиоза
 
Hypermedia api (HATEOAS)
Hypermedia api (HATEOAS)Hypermedia api (HATEOAS)
Hypermedia api (HATEOAS)
 
Сидристый Станислав: Паттерны и антипаттерны BDD
Сидристый Станислав: Паттерны и антипаттерны BDDСидристый Станислав: Паттерны и антипаттерны BDD
Сидристый Станислав: Паттерны и антипаттерны BDD
 
PageObject
PageObject PageObject
PageObject
 
Command Query Responsibility Segregation and Event Sourcing
Command Query Responsibility Segregation and Event SourcingCommand Query Responsibility Segregation and Event Sourcing
Command Query Responsibility Segregation and Event Sourcing
 
Взаимодействие сервисов через Advanced Message
 Queuing Protocol
Взаимодействие сервисов через Advanced Message
 Queuing ProtocolВзаимодействие сервисов через Advanced Message
 Queuing Protocol
Взаимодействие сервисов через Advanced Message
 Queuing Protocol
 

Similar to Антипаттерны модульного тестирования

Controller Testing: You're Doing It Wrong
Controller Testing: You're Doing It WrongController Testing: You're Doing It Wrong
Controller Testing: You're Doing It Wrongjohnnygroundwork
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test communityKerry Buckley
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Konstantin Kudryashov
 
How to Write Better Code with Mutation Testing
How to Write Better Code with Mutation TestingHow to Write Better Code with Mutation Testing
How to Write Better Code with Mutation TestingJohn Backus
 
Testing web APIs
Testing web APIsTesting web APIs
Testing web APIsFDConf
 
RSpock Testing Framework for Ruby
RSpock Testing Framework for RubyRSpock Testing Framework for Ruby
RSpock Testing Framework for RubyBrice Argenson
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl TechniquesDave Cross
 
Getting Answers to Your Testing Questions
Getting Answers to Your Testing QuestionsGetting Answers to Your Testing Questions
Getting Answers to Your Testing Questionsjasnow
 
Uses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsUses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsPatchSpace Ltd
 
Testing Has Many Purposes
Testing Has Many PurposesTesting Has Many Purposes
Testing Has Many PurposesAlex Sharp
 
Droidcon ES '16 - How to fail going offline
Droidcon ES '16 - How to fail going offlineDroidcon ES '16 - How to fail going offline
Droidcon ES '16 - How to fail going offlineJavier de Pedro López
 
The First C# Project Analyzed
The First C# Project AnalyzedThe First C# Project Analyzed
The First C# Project AnalyzedPVS-Studio
 
Model of the colossus @ Rupy Brazil 2013
Model of the colossus @ Rupy Brazil 2013 Model of the colossus @ Rupy Brazil 2013
Model of the colossus @ Rupy Brazil 2013 Mauro George
 

Similar to Антипаттерны модульного тестирования (20)

Controller Testing: You're Doing It Wrong
Controller Testing: You're Doing It WrongController Testing: You're Doing It Wrong
Controller Testing: You're Doing It Wrong
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Mocketry
MocketryMocketry
Mocketry
 
Python Homework Help
Python Homework HelpPython Homework Help
Python Homework Help
 
Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test community
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015
 
Why ruby
Why rubyWhy ruby
Why ruby
 
Rspec
RspecRspec
Rspec
 
How to Write Better Code with Mutation Testing
How to Write Better Code with Mutation TestingHow to Write Better Code with Mutation Testing
How to Write Better Code with Mutation Testing
 
Testing web APIs
Testing web APIsTesting web APIs
Testing web APIs
 
RSpock Testing Framework for Ruby
RSpock Testing Framework for RubyRSpock Testing Framework for Ruby
RSpock Testing Framework for Ruby
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
Testing in Django
Testing in DjangoTesting in Django
Testing in Django
 
Getting Answers to Your Testing Questions
Getting Answers to Your Testing QuestionsGetting Answers to Your Testing Questions
Getting Answers to Your Testing Questions
 
Why Our Code Smells
Why Our Code SmellsWhy Our Code Smells
Why Our Code Smells
 
Uses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsUses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & Stubs
 
Testing Has Many Purposes
Testing Has Many PurposesTesting Has Many Purposes
Testing Has Many Purposes
 
Droidcon ES '16 - How to fail going offline
Droidcon ES '16 - How to fail going offlineDroidcon ES '16 - How to fail going offline
Droidcon ES '16 - How to fail going offline
 
The First C# Project Analyzed
The First C# Project AnalyzedThe First C# Project Analyzed
The First C# Project Analyzed
 
Model of the colossus @ Rupy Brazil 2013
Model of the colossus @ Rupy Brazil 2013 Model of the colossus @ Rupy Brazil 2013
Model of the colossus @ Rupy Brazil 2013
 

Антипаттерны модульного тестирования

  • 2. О себе • Ruby on Rails разработчик • 4 года практики в стиле test-driven development • http://novembermeeting.blogspot.com
  • 3. Indented test code vs Правило Шапокляк describe PopularityCalculator, '#popular?' do it 'should take into account the comment count' do subject.popular?(post).should be_false end # ... end
  • 4. Indented test code vs Правило Шапокляк class PopularityCalculator def popular?(post) end end
  • 5. Indented test code vs Правило Шапокляк it 'should take into account the comment count' do posts = (0..19).map do |n| post_with_comment_count n end posts.each do |post| if 10 < post.comment_count subject.popular?(post).should be_true else subject.popular?(post).should be_false end end end
  • 6. Indented test code vs Правило Шапокляк class PopularityCalculator THRESHOLD = 10 def popular?(post) THRESHOLD < post.comment_count end end
  • 7. Indented test code vs Правило Шапокляк it 'should take into account the comment count' do posts = (0..19).map do |n| post_with_comment_count n end posts.each do |post| if 10 < post.comment_count subject.popular?(post).should be_true else subject.popular?(post).should be_false end end end
  • 8. Indented test code vs Правило Шапокляк Мотивация: • борьба с дублированием • работы с неконтролируемыми аспектами системы(время, дисковое пространство и т.д.)
  • 9. Indented test code vs Правило Шапокляк Правило Шапокляк: Это хорошо ... хорошо, что Вы зеленый и плоский
  • 10. Indented test code vs Правило Шапокляк it "should return true if the comment count / is more then the popularity threshold" do post = post_with_comment_count THRESHOLD + 1 subject.popular?(post).should be_true post = post_with_comment_count THRESHOLD + 100 subject.popular?(post).should be_true end
  • 11. Indented test code vs Правило Шапокляк Бенефиты: • тесты проще понять • тесты содержат меньше ошибок
  • 12. Production Logic in Test class PopularityCalculator THRESHOLD = 10 def popular?(post) THRESHOLD < post.comment_count end end
  • 13. Production Logic in Test it "should take into account the comment count" do post = post_with_comment_count 11 expected = THRESHOLD < post.comment_count actual = subject.popular? post actual.should == expected end
  • 14. Production Logic in Test it "should take into account the comment count" do post = post_with_comment_count 11 expected = THRESHOLD < post.comment_count actual = subject.popular? post actual.should == expected end
  • 15. Production Logic in Test Мотивация: упростить получение ожидаемого значения
  • 17. Production Logic in Test it "should take into account the comment count" do actual = subject.popular? post_with_comment_count(11) actual.should be_true end
  • 18. Production Logic in Test Бенефиты: мы действительно тестируем, а не только улучшаем тестовое покрытие :)
  • 19. Too Many Expectations describe NotificationService, "#notify_about" do it "should notify the post author by email" do notification_service.notify_about @comment end it "should notify the post author by sms" end
  • 20. Too Many Expectations class NotificationService < Struct.new(:email_service, :sms_service) def notify_about(comment) end end
  • 21. Too Many Expectations before do @email_service, @sms_service = mock, mock @comment = Comment.new 'some text', 'dummy post' @notification_service = NotificationService. new @email_service, @sms_service end
  • 22. Too Many Expectations it "should notify the post author by email" do @email_service.expects(:deliver_new_comment_email). with @comment @sms_service.expects :deliver_new_comment_sms @notification_service.notify_about @comment end Too Many Expectations it "should notify the post author by sms" do @email_service.expects :deliver_new_comment_email @sms_service.expects(:deliver_new_comment_sms). with @comment @notification_service.notify_about @comment end
  • 23. Too Many Expectations def notify_about(comment) email_service.deliver_new_comment_email comment sms_service.deliver_new_comment_sms comment end
  • 24. Too Many Expectations it "should notify the post author by email" do @email_service.expects(:deliver_new_comment_email). with @comment @sms_service.expects :deliver_new_comment_sms @notification_service.notify_about @comment end it "should notify the post author by sms" do @email_service.expects :deliver_new_comment_email @sms_service.expects(:deliver_new_comment_sms). with @comment @notification_service.notify_about @comment end
  • 25. Too Many Expectations def notify_about(comment) # email_service.deliver_new_comment_email comment sms_service.deliver_new_comment_sms comment end
  • 26. Too Many Expectations 1) Mocha::ExpectationError in 'NotificationService#notify_about should notify the post author by email' not all expectations were satisfied unsatisfied expectations: - expected exactly once, not yet invoked: #<Mock:0xb74cdd64>.deliver_new_comment_email(#<Comment:0xb74cdb70>, #<Mock:0xb74cdf08>) satisfied expectations: - expected exactly once, already invoked once: #<Mock:0xb74cde2c>.get(any_parameters) - expected exactly once, already invoked once: #<Mock:0xb74cdc9c>.author_id(any_parameters) - expected exactly once, already invoked once: nil.deliver_new_comment_sms(any_parameters) 2) Mocha::ExpectationError in 'NotificationService#notify_about should notify the post author by sms' not all expectations were satisfied unsatisfied expectations: - expected exactly once, not yet invoked: #<Mock:0xb74c937c>.deliver_new_comment_email(any_parameters) satisfied expectations: - expected exactly once, already invoked once: #<Mock:0xb74c9444>.get(any_parameters) - expected exactly once, already invoked once: #<Mock:0xb74c92b4>.author_id(any_parameters) - expected exactly once, already invoked once: nil.deliver_new_comment_sms(#<Comment:0xb74c9188>, #<Mock:0xb74c9520>)
  • 28. Too Many Expectations Причины воспроизведения паттерна: отсутствие знаний о стаб-объектах
  • 29. Too Many Expectations before do @sms_service = stub_everything @email_service = stub_everything @notification_service = NotificationService. new @email_service, @sms_service end
  • 30. Too Many Expectations it "should notify the post author by email" do @email_service.expects(:deliver_new_comment_email). with @comment, @author @notification_service.notify_about @comment end it "should notify the post author by sms" do @sms_service.expects(:deliver_new_comment_sms). with @comment, @author @notification_service.notify_about @comment end
  • 31. Too Many Expectations Бенефиты: одна ошибка - один падающий тест
  • 32. Redundant Fixture describe PostRepository, "#popular" do it "should return all popular posts" do repository.popular.should include(popular_post) end end
  • 33. Redundant Fixture class PostRepository def popular [] end end
  • 34. Redundant Fixture it "should return all popular posts" do popular_posts = (1..2).map { build_popular_post } unpopular_posts = (1..3). map { build_unpopular_post } posts = (popular_posts + unpopular_posts).shuffle repository = PostRepository.new posts actual = repository.popular actual.should have(2).posts actual.should include(popular_posts.first, popular_posts.last) end
  • 35. Redundant Fixture it "should return all popular posts" do popular_posts = (1..2).map { build_popular_post } unpopular_posts = (1..3). map { build_unpopular_post } posts = (popular_posts + unpopular_posts).shuffle repository = PostRepository.new posts actual = repository.popular actual.should have(2).posts actual.should include(popular_posts.first, popular_posts.last) end
  • 36. Redundant Fixture Мотивация: желание получить в тестовом окружении “реальные” данные
  • 38. Redundant Fixture before do @popular_post = build_popular_post @unpopular_post = build_unpopular_post @repository = PostRepository.new( [@popular_post, @unpopular_post] ) end it "should return a popular post" do @repository.popular.should include(@popular_post) end it "shouldn't return an unpopular post" do @repository.popular. should_not include(@unpopular_post) end
  • 39. Redundant Fixture Бенефиты: • простой setup • сообщение о падении теста не перегружено лишними данными • профилактика "медленных" тестов
  • 40. Neglected Diagnostic vs Ясный красный describe BullshitProfitCalculator, "#calculate" do it "should return the projected profit" do actual = subject.calculate 'dummy author' actual.should == '$123'.to_money end end class BullshitProfitCalculator def calculate(author) '$1'.to_money end end
  • 41. Neglected Diagnostic vs Ясный красный 'BullshitProfitCalculator#calculate should return the projected profit' FAILED expected: #<Money:0xb7447ebc @currency=#<Money::Currency id: usd priority: 1, iso_code: USD, name: United States Dollar, symbol: $, subunit: Cent, subunit_to_unit: 100, separator: ., delimiter: ,>, @cents=12300, @bank=#<Money::VariableExchangeBank:0xb74dabb8 @rates={}, @mutex=#<Mutex:0xb74dab7c>, @rounding_method=nil>>, got: #<Money:0xb7448038 @currency=#<Money::Currency id: usd priority: 1, iso_code: USD, name: United States Dollar, symbol: $, subunit: Cent, subunit_to_unit: 100, separator: ., delimiter: ,>, @cents=100, @bank=#<Money::VariableExchangeBank:0xb74dabb8 @rates={}, @mutex=#<Mutex:0xb74dab7c>, @rounding_method=nil>> (using ==)
  • 42. Neglected Diagnostic vs Ясный красный
  • 43. Neglected Diagnostic vs Ясный красный module TestMoneyFormatter def inspect format end end class Money include TestMoneyFormatter end
  • 44. Neglected Diagnostic vs Ясный красный 'BullshitProfitCalculator#calculate should return the projected profit' FAILED expected: $123.00, got: $1.00 (using ==)
  • 45. Neglected Diagnostic vs Ясный красный Было: Стало: красный красный ясный красный зеленый зеленый рефакторинг рефакторинг
  • 46. Neglected Diagnostic vs Ясный красный Бенефиты: ясное диагностическое сообщение упрощает дальнейшую поддержку теста
  • 47. Еще антипаттерны • глобальные фикстуры • функциональный код, используемый только в тестах • нарушение изоляции тестов • зависимости из других слоев приложения • тестирование кода фреймворка
  • 48. Антипаттерны в mocking TDD • мокание методов тестируемого модуля • мокание объектов-значений
  • 50. Рекомендуемая литература • Экстремальное программирование. Разработка через тестирование, Кент Бек • Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce
  • 52. Использованные изображения • http://dreamworlds.ru/kartinki/27030-otricatelnye- personazhi-v-multfilmakh.html • http://www.inquisitr.com/39089/former-police-officer- sues-for-discrimination-over-his-alcoholism-disability/ • http://teachpro.ru/source/obz11/Html/der11163.htm • http://bigpicture.ru/?p=4302 • http://giga.satel.com.ua/index.php?newsid=25654