SlideShare a Scribd company logo
1 of 69
Download to read offline
TESTING SURVIVAL GUIDE
                                                        Thilo Utke




A high level view on how to keep sane while doing tdd
WHY DO WE TEST?




why we take this extra step
TESTING CONS


                                   Write Extra Code

                                   Interrupt the Flow

                       Add Complexity to our Codebase

                                  More Maintenance



the negative effects are either blends or can be minimized
TESTING PROS

                                  Think Double

                                  Avoid Errors

                                Narrow Down Bugs

                                Prevent Regression

                                 Improve Design


This is what I get out of TDD
Failing Scenarios:

  cucumber features/currency_conversion.feature:129 # Scenario: Using the web-
  service supplied exchange rate for XAU

  cucumber features/statistics.feature:161 # Scenario: view statistics for completed
  withdrawals

  cucumber features/statistics.feature:189 # Scenario: view statistics for completed
  deposits

  cucumber features/transaction_confirmation.feature:6 # Scenario: Fund transactions
  must be confirmed before the funding is added to the balance




In situations like this I ask myself the following questions
BUG IN CODE?
BUG IN TEST?
EXTERNAL DEPENDENCY?
A NEW CUCUMBER VERSION?
A BUG IN RUBY BIG DECIMAL!




forget to use rvm ruby
TESTS GIVE US CONFIDENCE
ALLOW US TO MOVE FORWARD




IMHO Lack of confidence reason why software in Enterprise tend to become outdated
Not to philosophical
DOING TDD WITH RUBY IS
        EASY
class Test{

     //JMock & JUnit

     public void testWithCategoryNameGetNameOfPostCategory(){
       final Category category = context.mock(Category.class) //MockObjects
       final Post post = context.mock(Post.class)

         oneOf (post).name; will(returnValue('TestPost')); //Stub Methods
         oneOf (post).category; will(returnValue(category))

         context.checking(new Expectations() {{ //Mock
            oneOf (category).name; will(returnValue('TestCat'))
         }}

         PostNamesWithCategoryLoader loader = new PostNamesWithCategoryLoader

         context.assertIsSatisfied();

     }
 }


might be improved a little (anotations?)
# mocha

 def test_show_gets_name_of_post_category
   category = mock('category', :name => 'test') # mock, name must be called
   post = stub('post', :category => category) #stub
   Post.stubs(:find).returns(post) #partial stub for find
   get :show
 end




Ruby the better Language for Testing DSL
DOING TDD RIGHT IS HARD




How often do you swear at your breaking test?
How often does you feel that tests break your flow?
Simple

                             Fast

                        Maintainable

                          Durable

                       Side-effect Free

                         Repeatable



Thats what you want.
GUIDELINES NOT RULES




1. Part
I assume the basics like using setups, cleaning up
mostly unittests
1. SIMPLICITY




Break down the domain problem in no brainers
THINK DOUBLE


      What I want     Presentation

     Required Info    Controller

How get these Info    DomainModel
STORY



    For customer information I want a listing of payment providers
    and their logos. Their logo should be linked when I provide a
    link. If their is no logo, show the link instead.




How many started to think of it as one problem to solve?
Split it up in separate problems.
REDUCE TO NO-BRAINER
                            LOGO                        URL

                               X                         X

                               X

                                                         X




Do this on complex problems and you will implement them easier, sometimes this will leave
you wondering.
BE EXPLICIT

 it "should show the description for a payment provider" do
   payme = PaymentProvider.new(description: 'Pay me')
   payment_provider_listing(payme).should include(payme.description)
 end




Not a good Idea.
BE EXPLICIT


  it "should show the description for a payment provider" do
    payme = PaymentProvider.new(description: 'Pay me')
    payment_provider_listing(payme).should include('Pay me')
  end




Be explicit.
1 THING AT A TIME


  payment_provider_listing(payme).should ==
  '<a href="pay.me">PayMe</a>, Get Paid'

  VS

  payment_provider_listing(payme).should
  include('<a href="pay.me">PayMe</a>')




That also relates to another problem
2. DURABILITY
1 THING AT A TIME


payment_provider_listing(payme).should ==
'<a href="pay.me">PayMe</a>, Get Paid'

VS

payment_provider_listing(payme).should
include('<a href="pay.me">PayMe</a>')
RELAX SPECIFICATION

  payment_provider_listing(payme).should
  match(/<a.*href="pay.me".*>PayMe</a>/)




Improves durability but not so easy to read anymore. Some things contradict.
RELAX SPECIFICATION
 it "should initialize the correct gateway with the order" do
   order = Order.make(:ecurrency => 'GoldPay')
   GoldPayGateway.should_receive(:new).with(order, anything)
   GatewayFactory.build(order)
 end




Next Topic
MAINTAINABILITY
it "should return the last created owner as the current owner" do
      gateway = Gateway.create!(api_key: 'XYZ', url: 'api.pay.me')
      provider = PaymentProvider.create!(url: 'pay.me', name: 'Payme', gateway: gateway)
      owner_1 = Owner.create!(first_name: 'Phil', name: 'Adams', provider: provider)
      owner_2 = Owner.create!(first_name: 'Maria', name: 'Williams', provider: provider)
      provider.current_owner.should == owner_2
    end




This is not good!
it "should return the last created owner as the current owner" do
      @provider_with_two_owners.current_owner.should == @owner_2
    end




This neither
You ask why?
2. CONTEXT




Context is important
What are you dealing with
TOO NOISY


 it "should return the last created owner as the current owner" do
   gateway = Gateway.create!(api_key: 'XYZ', url: 'api.pay.me')
   provider = PaymentProvider.create!(url: 'pay.me', name: 'Payme', gateway: gateway)
   owner_1 = Owner.create!(first_name: 'Phil', last_name: 'Adams', provider: provider)
   owner_2 = Owner.create!(first_name: 'Maria', last_name: 'Williams', provider: provider)
   provider.current_owner.should == owner_2
 end




To much
NO CONTEXT



     it "should return the last created owner as the current owner" do
       @provider_with_two_owners.current_owner.should == @owner_2
     end




To little
MAINTAIN CONTEXT


    it "should return the last created owner as the current owner" do
      provider = Provider.make
      owner_1 = Owner.make provider: provider
      owner_2 = Owner.make provider: provider
      provider.current_owner.should == owner_2
    end




right amout
SPLIT SETUP TO DRY
                     CONTEXT
  describe 'transaction' do
    before(:each) do
      @payer = User.make
    end

    describe "percentage payout bonus set" do
      before(:each) do
        PayoutBonus.create!(amount: 20, unit: :percentage)
      end
    end

    describe "fixed payout bonus set" do
      before(:each) do
        PayoutBonus.create!(amount: 10, unit: :usd)
      end
    end
  end

another contradiction because this also increases creates complexity by adding new places
BETTER UNDERSTANDABLE
             THAN DRY



others have to work with your code
3. SPEED




Most important for unit tests as you run them over and over again
TEST IN ISOLATION


  it "should be false when order has a single product from a single partner" do
    partner = Partner.make
    product = Product.make new_partner_name: partner.name
    order = Order.make_unsaved partner: partner, contact: Contact.make, address: Address.make
    order.items.build product: product, price: 100, scale_basis: 1, quantity: 1
    order.save!
    order.reload
    order.should_not have_multiple_product_partners
  end




Most speed is gained if only that code executes that is necessary for that test
TEST IN ISOLATION


      it "should be false when order has a single product from a single partner" do
    product = Product.make new_partner_name: "Pear"
    order = Order.make_unsaved
    order.items << OrderItem.make_unsaved :product = product
    order.should_not have_multiple_product_partners
  end




Most speed is gained if only that code executes that is necessary for that test
ISOLATION THROUGH
                  MOCKING



Instead of real dependencies inject mocks
different techniques
FAKES



 • Mimic  the behavior of the real object but don’t share all
    characteristics




good example are in memory data storage vs. persistence.
FAKE USAGE
   class FakeActivityLogger
  def log(object)
    @changes[object.id] ||= []
    @changes[object.id] << object.changes
  end

  def changes_for(object)
    @changes[object.id]
  end
end

it "should call loggers on changes" do
  logger = FakeActivityLogger.new
  @logger_config.register(logger, User)
  user = User.make(name: 'Paul')
  user.update_attribute(:name, 'Paula')
  logger.changes_for(user).should = [:name, 'Paul', 'Paula']
end
STUBS



• Pretend   to be some object but without any logic
STUB USAGE

it "should change the given attribute" do
  logger = stub('stub_logger', log: true)
  @logger_config.register(logger, User)
  user = User.make(name: 'Paul')
  user.update_attribute(:name, 'Paula')
  user.name.should == 'Paula'
end
MOCKS



• Pretend to be some object, also no logic but monitor if
 interaction with them is specified
MOCK USAGE


it "should call loggers on changes" do
  logger = mock('mock_logger')
  @logger_config.register(logger, User)
  user = User.make(name: 'Paul')
  logger.expects(:log).with(user).once
  user.update_attribute(:name, 'Paula')
end
MOCKS AND FAKES CAN
        HIDE INTEGRATION BUGS



Integration or Acceptancetests to the rescue
excessive use of mocking my counteract fast testing if more integration test is required
WRONG USAGE OF MOCKS
   HURT DURABILITY
MOCK BEHAVIOR UNDERT
      TEST AND STUB THE REST



Rule of thump
LISTEN TO YOUR TESTS
            “If something hurts you probably doing it wrong”




examples taken from real code I was involved.
TO MANY DEPENDENCIES

 it "should be false when order has a single product from a single partner" do
   partner = Partner.make
   product = Product.make new_partner_name: partner.name
   order = Order.make_unsaved partner: partner, contact: Contact.make, address: Address.make
   order.items.build product: product, price: 100, scale_basis: 1, quantity: 1
   order.save!
   order.reload
   order.should_not have_multiple_product_partners
 end




Bad Design
TO MANY DEPENDENCIES


• split   up

• add     layer

• decouple        logic
MANY MOCKS / SETUP FOR
           INTERNALS
  before(:each) do
    @converter = mock_model CurrencyConverter, convert: 4, exchange_fee: 4, convertible?: true
    CurrencyConverter.stub!(:new).and_return(@converter)
    @modified_converter = mock_model ModifiedCurrencyConverter convert: 200
    ModifiedCurrencyConverter.stub!(:new).and_return(@modified_converter)
    @user = mock_model(User, cleared_balance: 1000, add_balance_transaction: true,
      request_balance_transaction: true)
    @currency_conversion = CurrencyConversion.new source_amount: 100, source_currency: 'USD',
      destination_currency: 'EUR', user: @user
  end

  it "should request source amount plus fee from users source currency balance" do
    @user.should_receive(:request_balance_transaction).with(@currency_conversion, 104, 'USD')
    @currency_conversion.save!
  end




5 mocks/partial mocks
INTERNAL DEPENDENCIES



• Inject   Dependencies

• Iffrom callbacks, use a observer or think about Presenter/
  Service
STUB CHAINS/METHOD
                 CHAINS

 it "should be false when order has a single product from a single partner" do
   order = Order.make_unsaved
   item = OrderItem.make_unsaved :name => 'iPet'
   order.stub_chain(:items, :delivered, :from_partner, :last => item)
   last_delivered_item_for_partner_label(partner, order).should include('iPed')
 end




artificial
exposes to many internals
EXPLAINING COMMENTS ON
       EXPECTATIONS

it "should sum all credits for the partner" do
  credit1 = @partner.credits.make(:order => @order, :payment => 100)
  credit1.items.make :price => 100, :quantity => 1
  credit2 = @partner.credits.make(:order => @order, :payment => 100)
  credit2.items.make :price => 150, :quantity => 1
  @partner.credits.sum_for_month(Date.today.month, Date.today.year).should ==
    297.5 # including 19% tax
end
TOOLS




help to write faster/better tests
beside your test/mock framework of choice
MORE INFRASTRUCTURE




more to maintain
BENEFIT > COST ?
SPORK


  + Reduce  startup time for testing frameworks (RSpec,
      Cucumber, Test-Unit)

  -   Reloading breaks for code loaded in environment/
      initializers




Great timesaver in unittest for bigger projects with lot of gems and plugins
BUNDLER


  + full   dependency resolution at once

  + version    lockdown

  -   beta




fixed version are great, no surprises with unexpected updates.
FACTORIES
    (MACHINIST, FACTORY GIRL)

+ Greatly   remove noise in tests

+ Dry   Setups

+ Keep   Context

-   DB Overhead
HYDRA/PARALLEL SPEC


  + distribute    tests on multiple cores or even machines

  - extra setup

  - concurrency/load order issues




So far no serious project running with them
CAPYBARA


 + Allow     to run cucumber features agains different backends

 + full   stack testing with culerity or selenium where required

 -   not one feature on many backends




setup is a super easy with cucumber
WEBMOCK/SHAM_RACK



+ Allow   to fake or mock http apis

-   Don’t tell you when the real api changes ;)
ENVIRONMENTS



  + Allow    to isolate tools completely

  - Extra startup time




cucumber is doing it, you can do this too.
INFO


• Name: Thilo   Utke

• Company: Upstream-Agile   GmbH

• Web: http://upstre.am

• Twitter: @freaklikeme

More Related Content

Similar to Testing survival Guide

Code Quality Practice and Tools
Code Quality Practice and ToolsCode Quality Practice and Tools
Code Quality Practice and ToolsBob Paulin
 
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
 
Building high productivity applications
Building high productivity applicationsBuilding high productivity applications
Building high productivity applicationsHutomo Sugianto
 
Cis407 a ilab 5 web application development devry university
Cis407 a ilab 5 web application development devry universityCis407 a ilab 5 web application development devry university
Cis407 a ilab 5 web application development devry universitylhkslkdh89009
 
SELJE_Database_Unit_Testing.pdf
SELJE_Database_Unit_Testing.pdfSELJE_Database_Unit_Testing.pdf
SELJE_Database_Unit_Testing.pdfEric Selje
 
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
 
Hibernate Performance Tuning (JEEConf 2012)
Hibernate Performance Tuning (JEEConf 2012)Hibernate Performance Tuning (JEEConf 2012)
Hibernate Performance Tuning (JEEConf 2012)Sander Mak (@Sander_Mak)
 
Building Better Applications with Data::Manager
Building Better Applications with Data::ManagerBuilding Better Applications with Data::Manager
Building Better Applications with Data::ManagerJay Shirley
 
Continously delivering
Continously deliveringContinously delivering
Continously deliveringJames Cowie
 
[2012 02 03]clean_code 4장
[2012 02 03]clean_code 4장[2012 02 03]clean_code 4장
[2012 02 03]clean_code 4장Jong Pil Won
 
Save time by applying clean code principles
Save time by applying clean code principlesSave time by applying clean code principles
Save time by applying clean code principlesEdorian
 
Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e bigAndy Peterson
 
Software Testing & PHPSpec
Software Testing & PHPSpecSoftware Testing & PHPSpec
Software Testing & PHPSpecDarren Craig
 
Lotusphere 2007 AD505 DevBlast 30 LotusScript Tips
Lotusphere 2007 AD505 DevBlast 30 LotusScript TipsLotusphere 2007 AD505 DevBlast 30 LotusScript Tips
Lotusphere 2007 AD505 DevBlast 30 LotusScript TipsBill Buchan
 
2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD Workshop2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD WorkshopWolfram Arnold
 
Rails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power StackRails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power StackDavid Copeland
 
WordCamp SF 2011: Debugging in WordPress
WordCamp SF 2011: Debugging in WordPressWordCamp SF 2011: Debugging in WordPress
WordCamp SF 2011: Debugging in WordPressandrewnacin
 

Similar to Testing survival Guide (20)

Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
 
Code Quality Practice and Tools
Code Quality Practice and ToolsCode Quality Practice and Tools
Code Quality Practice and Tools
 
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
 
Building high productivity applications
Building high productivity applicationsBuilding high productivity applications
Building high productivity applications
 
Cis407 a ilab 5 web application development devry university
Cis407 a ilab 5 web application development devry universityCis407 a ilab 5 web application development devry university
Cis407 a ilab 5 web application development devry university
 
SELJE_Database_Unit_Testing.pdf
SELJE_Database_Unit_Testing.pdfSELJE_Database_Unit_Testing.pdf
SELJE_Database_Unit_Testing.pdf
 
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
 
Hibernate Performance Tuning (JEEConf 2012)
Hibernate Performance Tuning (JEEConf 2012)Hibernate Performance Tuning (JEEConf 2012)
Hibernate Performance Tuning (JEEConf 2012)
 
Building Better Applications with Data::Manager
Building Better Applications with Data::ManagerBuilding Better Applications with Data::Manager
Building Better Applications with Data::Manager
 
UI Testing
UI TestingUI Testing
UI Testing
 
Continously delivering
Continously deliveringContinously delivering
Continously delivering
 
[2012 02 03]clean_code 4장
[2012 02 03]clean_code 4장[2012 02 03]clean_code 4장
[2012 02 03]clean_code 4장
 
Save time by applying clean code principles
Save time by applying clean code principlesSave time by applying clean code principles
Save time by applying clean code principles
 
Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e big
 
Php tests tips
Php tests tipsPhp tests tips
Php tests tips
 
Software Testing & PHPSpec
Software Testing & PHPSpecSoftware Testing & PHPSpec
Software Testing & PHPSpec
 
Lotusphere 2007 AD505 DevBlast 30 LotusScript Tips
Lotusphere 2007 AD505 DevBlast 30 LotusScript TipsLotusphere 2007 AD505 DevBlast 30 LotusScript Tips
Lotusphere 2007 AD505 DevBlast 30 LotusScript Tips
 
2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD Workshop2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD Workshop
 
Rails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power StackRails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power Stack
 
WordCamp SF 2011: Debugging in WordPress
WordCamp SF 2011: Debugging in WordPressWordCamp SF 2011: Debugging in WordPress
WordCamp SF 2011: Debugging in WordPress
 

Recently uploaded

Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 

Recently uploaded (20)

Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 

Testing survival Guide

  • 1. TESTING SURVIVAL GUIDE Thilo Utke A high level view on how to keep sane while doing tdd
  • 2. WHY DO WE TEST? why we take this extra step
  • 3. TESTING CONS Write Extra Code Interrupt the Flow Add Complexity to our Codebase More Maintenance the negative effects are either blends or can be minimized
  • 4. TESTING PROS Think Double Avoid Errors Narrow Down Bugs Prevent Regression Improve Design This is what I get out of TDD
  • 5. Failing Scenarios: cucumber features/currency_conversion.feature:129 # Scenario: Using the web- service supplied exchange rate for XAU cucumber features/statistics.feature:161 # Scenario: view statistics for completed withdrawals cucumber features/statistics.feature:189 # Scenario: view statistics for completed deposits cucumber features/transaction_confirmation.feature:6 # Scenario: Fund transactions must be confirmed before the funding is added to the balance In situations like this I ask myself the following questions
  • 9. A NEW CUCUMBER VERSION?
  • 10. A BUG IN RUBY BIG DECIMAL! forget to use rvm ruby
  • 11. TESTS GIVE US CONFIDENCE
  • 12. ALLOW US TO MOVE FORWARD IMHO Lack of confidence reason why software in Enterprise tend to become outdated Not to philosophical
  • 13. DOING TDD WITH RUBY IS EASY
  • 14. class Test{ //JMock & JUnit public void testWithCategoryNameGetNameOfPostCategory(){ final Category category = context.mock(Category.class) //MockObjects final Post post = context.mock(Post.class) oneOf (post).name; will(returnValue('TestPost')); //Stub Methods oneOf (post).category; will(returnValue(category)) context.checking(new Expectations() {{ //Mock oneOf (category).name; will(returnValue('TestCat')) }} PostNamesWithCategoryLoader loader = new PostNamesWithCategoryLoader context.assertIsSatisfied(); } } might be improved a little (anotations?)
  • 15. # mocha def test_show_gets_name_of_post_category category = mock('category', :name => 'test') # mock, name must be called post = stub('post', :category => category) #stub Post.stubs(:find).returns(post) #partial stub for find get :show end Ruby the better Language for Testing DSL
  • 16. DOING TDD RIGHT IS HARD How often do you swear at your breaking test? How often does you feel that tests break your flow?
  • 17. Simple Fast Maintainable Durable Side-effect Free Repeatable Thats what you want.
  • 18. GUIDELINES NOT RULES 1. Part I assume the basics like using setups, cleaning up mostly unittests
  • 19. 1. SIMPLICITY Break down the domain problem in no brainers
  • 20. THINK DOUBLE What I want Presentation Required Info Controller How get these Info DomainModel
  • 21. STORY For customer information I want a listing of payment providers and their logos. Their logo should be linked when I provide a link. If their is no logo, show the link instead. How many started to think of it as one problem to solve? Split it up in separate problems.
  • 22. REDUCE TO NO-BRAINER LOGO URL X X X X Do this on complex problems and you will implement them easier, sometimes this will leave you wondering.
  • 23. BE EXPLICIT it "should show the description for a payment provider" do payme = PaymentProvider.new(description: 'Pay me') payment_provider_listing(payme).should include(payme.description) end Not a good Idea.
  • 24. BE EXPLICIT it "should show the description for a payment provider" do payme = PaymentProvider.new(description: 'Pay me') payment_provider_listing(payme).should include('Pay me') end Be explicit.
  • 25. 1 THING AT A TIME payment_provider_listing(payme).should == '<a href="pay.me">PayMe</a>, Get Paid' VS payment_provider_listing(payme).should include('<a href="pay.me">PayMe</a>') That also relates to another problem
  • 27. 1 THING AT A TIME payment_provider_listing(payme).should == '<a href="pay.me">PayMe</a>, Get Paid' VS payment_provider_listing(payme).should include('<a href="pay.me">PayMe</a>')
  • 28. RELAX SPECIFICATION payment_provider_listing(payme).should match(/<a.*href="pay.me".*>PayMe</a>/) Improves durability but not so easy to read anymore. Some things contradict.
  • 29. RELAX SPECIFICATION it "should initialize the correct gateway with the order" do order = Order.make(:ecurrency => 'GoldPay') GoldPayGateway.should_receive(:new).with(order, anything) GatewayFactory.build(order) end Next Topic
  • 31. it "should return the last created owner as the current owner" do gateway = Gateway.create!(api_key: 'XYZ', url: 'api.pay.me') provider = PaymentProvider.create!(url: 'pay.me', name: 'Payme', gateway: gateway) owner_1 = Owner.create!(first_name: 'Phil', name: 'Adams', provider: provider) owner_2 = Owner.create!(first_name: 'Maria', name: 'Williams', provider: provider) provider.current_owner.should == owner_2 end This is not good!
  • 32. it "should return the last created owner as the current owner" do @provider_with_two_owners.current_owner.should == @owner_2 end This neither You ask why?
  • 33. 2. CONTEXT Context is important What are you dealing with
  • 34. TOO NOISY it "should return the last created owner as the current owner" do gateway = Gateway.create!(api_key: 'XYZ', url: 'api.pay.me') provider = PaymentProvider.create!(url: 'pay.me', name: 'Payme', gateway: gateway) owner_1 = Owner.create!(first_name: 'Phil', last_name: 'Adams', provider: provider) owner_2 = Owner.create!(first_name: 'Maria', last_name: 'Williams', provider: provider) provider.current_owner.should == owner_2 end To much
  • 35. NO CONTEXT it "should return the last created owner as the current owner" do @provider_with_two_owners.current_owner.should == @owner_2 end To little
  • 36. MAINTAIN CONTEXT it "should return the last created owner as the current owner" do provider = Provider.make owner_1 = Owner.make provider: provider owner_2 = Owner.make provider: provider provider.current_owner.should == owner_2 end right amout
  • 37. SPLIT SETUP TO DRY CONTEXT describe 'transaction' do before(:each) do @payer = User.make end describe "percentage payout bonus set" do before(:each) do PayoutBonus.create!(amount: 20, unit: :percentage) end end describe "fixed payout bonus set" do before(:each) do PayoutBonus.create!(amount: 10, unit: :usd) end end end another contradiction because this also increases creates complexity by adding new places
  • 38. BETTER UNDERSTANDABLE THAN DRY others have to work with your code
  • 39. 3. SPEED Most important for unit tests as you run them over and over again
  • 40. TEST IN ISOLATION it "should be false when order has a single product from a single partner" do partner = Partner.make product = Product.make new_partner_name: partner.name order = Order.make_unsaved partner: partner, contact: Contact.make, address: Address.make order.items.build product: product, price: 100, scale_basis: 1, quantity: 1 order.save! order.reload order.should_not have_multiple_product_partners end Most speed is gained if only that code executes that is necessary for that test
  • 41. TEST IN ISOLATION it "should be false when order has a single product from a single partner" do product = Product.make new_partner_name: "Pear" order = Order.make_unsaved order.items << OrderItem.make_unsaved :product = product order.should_not have_multiple_product_partners end Most speed is gained if only that code executes that is necessary for that test
  • 42. ISOLATION THROUGH MOCKING Instead of real dependencies inject mocks different techniques
  • 43. FAKES • Mimic the behavior of the real object but don’t share all characteristics good example are in memory data storage vs. persistence.
  • 44. FAKE USAGE class FakeActivityLogger def log(object) @changes[object.id] ||= [] @changes[object.id] << object.changes end def changes_for(object) @changes[object.id] end end it "should call loggers on changes" do logger = FakeActivityLogger.new @logger_config.register(logger, User) user = User.make(name: 'Paul') user.update_attribute(:name, 'Paula') logger.changes_for(user).should = [:name, 'Paul', 'Paula'] end
  • 45. STUBS • Pretend to be some object but without any logic
  • 46. STUB USAGE it "should change the given attribute" do logger = stub('stub_logger', log: true) @logger_config.register(logger, User) user = User.make(name: 'Paul') user.update_attribute(:name, 'Paula') user.name.should == 'Paula' end
  • 47. MOCKS • Pretend to be some object, also no logic but monitor if interaction with them is specified
  • 48. MOCK USAGE it "should call loggers on changes" do logger = mock('mock_logger') @logger_config.register(logger, User) user = User.make(name: 'Paul') logger.expects(:log).with(user).once user.update_attribute(:name, 'Paula') end
  • 49. MOCKS AND FAKES CAN HIDE INTEGRATION BUGS Integration or Acceptancetests to the rescue excessive use of mocking my counteract fast testing if more integration test is required
  • 50. WRONG USAGE OF MOCKS HURT DURABILITY
  • 51. MOCK BEHAVIOR UNDERT TEST AND STUB THE REST Rule of thump
  • 52. LISTEN TO YOUR TESTS “If something hurts you probably doing it wrong” examples taken from real code I was involved.
  • 53. TO MANY DEPENDENCIES it "should be false when order has a single product from a single partner" do partner = Partner.make product = Product.make new_partner_name: partner.name order = Order.make_unsaved partner: partner, contact: Contact.make, address: Address.make order.items.build product: product, price: 100, scale_basis: 1, quantity: 1 order.save! order.reload order.should_not have_multiple_product_partners end Bad Design
  • 54. TO MANY DEPENDENCIES • split up • add layer • decouple logic
  • 55. MANY MOCKS / SETUP FOR INTERNALS before(:each) do @converter = mock_model CurrencyConverter, convert: 4, exchange_fee: 4, convertible?: true CurrencyConverter.stub!(:new).and_return(@converter) @modified_converter = mock_model ModifiedCurrencyConverter convert: 200 ModifiedCurrencyConverter.stub!(:new).and_return(@modified_converter) @user = mock_model(User, cleared_balance: 1000, add_balance_transaction: true, request_balance_transaction: true) @currency_conversion = CurrencyConversion.new source_amount: 100, source_currency: 'USD', destination_currency: 'EUR', user: @user end it "should request source amount plus fee from users source currency balance" do @user.should_receive(:request_balance_transaction).with(@currency_conversion, 104, 'USD') @currency_conversion.save! end 5 mocks/partial mocks
  • 56. INTERNAL DEPENDENCIES • Inject Dependencies • Iffrom callbacks, use a observer or think about Presenter/ Service
  • 57. STUB CHAINS/METHOD CHAINS it "should be false when order has a single product from a single partner" do order = Order.make_unsaved item = OrderItem.make_unsaved :name => 'iPet' order.stub_chain(:items, :delivered, :from_partner, :last => item) last_delivered_item_for_partner_label(partner, order).should include('iPed') end artificial exposes to many internals
  • 58. EXPLAINING COMMENTS ON EXPECTATIONS it "should sum all credits for the partner" do credit1 = @partner.credits.make(:order => @order, :payment => 100) credit1.items.make :price => 100, :quantity => 1 credit2 = @partner.credits.make(:order => @order, :payment => 100) credit2.items.make :price => 150, :quantity => 1 @partner.credits.sum_for_month(Date.today.month, Date.today.year).should == 297.5 # including 19% tax end
  • 59. TOOLS help to write faster/better tests beside your test/mock framework of choice
  • 62. SPORK + Reduce startup time for testing frameworks (RSpec, Cucumber, Test-Unit) - Reloading breaks for code loaded in environment/ initializers Great timesaver in unittest for bigger projects with lot of gems and plugins
  • 63. BUNDLER + full dependency resolution at once + version lockdown - beta fixed version are great, no surprises with unexpected updates.
  • 64. FACTORIES (MACHINIST, FACTORY GIRL) + Greatly remove noise in tests + Dry Setups + Keep Context - DB Overhead
  • 65. HYDRA/PARALLEL SPEC + distribute tests on multiple cores or even machines - extra setup - concurrency/load order issues So far no serious project running with them
  • 66. CAPYBARA + Allow to run cucumber features agains different backends + full stack testing with culerity or selenium where required - not one feature on many backends setup is a super easy with cucumber
  • 67. WEBMOCK/SHAM_RACK + Allow to fake or mock http apis - Don’t tell you when the real api changes ;)
  • 68. ENVIRONMENTS + Allow to isolate tools completely - Extra startup time cucumber is doing it, you can do this too.
  • 69. INFO • Name: Thilo Utke • Company: Upstream-Agile GmbH • Web: http://upstre.am • Twitter: @freaklikeme