SlideShare a Scribd company logo
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

Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
Mike Subelsky
 
Code Quality Practice and Tools
Code Quality Practice and ToolsCode Quality Practice and Tools
Code Quality Practice and Tools
Bob 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 Wrong
johnnygroundwork
 
Building high productivity applications
Building high productivity applicationsBuilding high productivity applications
Building high productivity applications
Hutomo 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.pdf
Eric 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
 
UI Testing
UI TestingUI Testing
UI Testing
Josh Black
 
Continously delivering
Continously deliveringContinously delivering
Continously delivering
James 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
 
Php tests tips
Php tests tipsPhp tests tips
Php tests tips
Damian Sromek
 
Software Testing & PHPSpec
Software Testing & PHPSpecSoftware Testing & PHPSpec
Software Testing & PHPSpec
Darren 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 Tips
Bill 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 Workshop
Wolfram 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 Stack
David Copeland
 
WordCamp SF 2011: Debugging in WordPress
WordCamp SF 2011: Debugging in WordPressWordCamp SF 2011: Debugging in WordPress
WordCamp SF 2011: Debugging in WordPress
andrewnacin
 

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

GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
Ralf Eggert
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Product School
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Ramesh Iyer
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 
"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
Fwdays
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
Cheryl Hung
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
CatarinaPereira64715
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 

Recently uploaded (20)

GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 

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