54
The hitchhicker’s guide to unit tes1ng RémyChristophe Schermesser @el_picador

The hitchhicker’s guide to unit testing

Embed Size (px)

DESCRIPTION

You already know TDD, your code coverage is almost at 80%, jUnit has no secret for you? But you feel that you could do more with your tests, the tools you use have limitations. Or maybe you're just tired of assertEquals? Don't panic! We will see how you can code tests. We will look upon: - Mutation Testing - BDD, Behaviour Driven Development - Property Testing

Citation preview

The  hitchhicker’s  guide  to  unit  tes1ng  

Rémy-­‐Christophe  Schermesser  @el_picador  

Ruby!  

Scala!  

Java!  

Python!  

PHP!  

Ruby!  

Scala!  

Java!  

Python!  

PHP!  

But  don’t  forget  your  towel  

Test::Unit  

ScalaTest  

jUnit  

uniFest  

PHPUnit  

Test::Unit  

ScalaTest  

jUnit  

uniFest  

PHPUnit  

Again  don’t  forget  your  towel  

JUnit  

assertEquals  

At  the  beginning,  we  had  

Then  

assertThatMyTestFitsOnOneLine(whatIExpect,  whatMyCodeIsDoing);  

And  

void  testWithCamelCaseToReadItBeFer()  {  ...  }  

And  again  

@Test  void  annotaZonsAreGoodForYourHealth()  {  ...  }  

And  again  again  

void  testMyTest()  {      Obj  obj  =  new  Obj();    //  10  lignes  of  things    assertEquals(…);  

}  

JUnit  

JUnit  

Mocks  

JUnit  

Fixtures  

Mocks  

JUnit  

Behavior  tes1ng  

with  RSpec  

Problem  

void  testMyTest()  {      Obj  obj  =  new  Obj();    //  10  lignes  of  things    assertEquals(…);  

}  

Behavior  tesZng,  don’t  test,  do  describe  

Describe  what  your  program  should  do  

One  test  One  (english)  sentence  

Behavior  tesZng,  don’t  test,  do  describe  

RSpec,  don’t  panic,  factorize  and  do  DSL  

describe  CompaniesController  do        describe  "POST  create"  do              context  "when  recruiter    

signed_in  with  no  company"  do                    …              end        end  end  

describe  CompaniesController  do        describe  "POST  create"  do              context  "when  recruiter    

signed_in  with  no  company"  do                    …              end        end  end  

describe  CompaniesController  do        describe  "POST  create"  do              context  "when  recruiter    

signed_in  with  no  company  an  email  is  sent"  do  

                 …              end        end  end  

let!(:recruiter)  {  login_recruiter  create(:recruiter,                  company_id:  nil)  }      context  "when  good  params"  do    let(:params)  {  …  }          it  {  expect  {  post  :create,  params  }.to  change(Company,  :count).by  1  }      it  {  expect  {  post  :create,  params  }.to          change(Ac1onMailer::Base.deliveries,  :count).by  2  }    end  

“When  recruiter  signed_in  with  no  company”

let!(:recruiter)  {  login_recruiter  create(:recruiter,  company_id:  nil)  }      context  "when  good  params"  do    let(:params)  {  …  }          it  {  expect  {  post  :create,  params  }.to  change(Company,  :count).by  1  }      it  {  expect  {  post  :create,  params  }.to          change(Ac1onMailer::Base.deliveries,  :count).by  2  }    end  

“When  recruiter  signed_in  with  no  company”

let!(:recruiter)  {  login_recruiter  create(:recruiter,  company_id:  nil)  }      context  "when  good  params"  do    let(:params)  {  …  }          it  {  expect  {  post  :create,  params  }.to  change(Company,  :count).by  1  }      it  {  expect  {  post  :create,  params  }.to          change(Ac1onMailer::Base.deliveries,  :count).by  2  }    end  

“When  recruiter  signed_in  with  no  company”

let!(:recruiter)  {  login_recruiter  create(:recruiter,  company_id:  nil)  }      context  "when  good  params"  do    let(:params)  {  …  }          it  {  expect  {  post  :create,  params  }.to                  change(Company,  :count).by  1  }      it  {  expect  {  post  :create,  params  }.to              change(Ac1onMailer::Base.deliveries,  :count).by  2  }    end  

“When  recruiter  signed_in  with  no  company”

Like  a  towel,  use  it  every  day  

JUnit  

Muta1on  tes1ng  

Behavior  tes1ng  

with  Javalanche  

Code  coverage  has  limits  Code:  ctrl+c  Test:  ctrl+v  

Code  a  class  and  test  it  

Mutate  it  

&&  ++  !=  >  …  

||  -­‐-­‐  ==  <  …  

Mutate  it  

if(a  &&  b)  {          i++;  }  else  {          i-­‐-­‐;  }  

if(a || b) {     i++; } else {     i--; }

if(a && b) {     i--; } else {     i--; }

Mutate  it  

Kill  it  

mvn  test  

ant  -­‐f  javalanche.xml  mutaZonTest  

Kill  it  

Run  tests  

Green  tests  

Something’s  wrong  

Run  tests  

Red  tests  

Great  job!  

Coverage  data  Equivalent  mutant  if(index  >=  10)  break  and  if(index  ==  10)  break  

Selec1ve  muta1on  

Parallel  execu1on  Choose  your  mutants  wisely  

Using  code  coverage  to  reduce  the  tests  to  run  

Speed-­‐up  

The  right  tool  

Behavior  tes1ng  

JUnit  

Property  tes1ng   Muta1on  

tes1ng  

with  ScalaCheck  

How  to  test  string  concatenaZon  ?  

assert(  ("ta"  +  "a")  ==  "taa"  )  

How  to  test  string  concatenaZon  ?  

assert(  ("ta"  +  "a")  ==  "taa"  )  assert(  ("ta"  +  "b")  ==  "tab"  )  

How  to  test  string  concatenaZon  ?  

assert(  ("ta"  +  "a")  ==  "taa"  )  assert(  ("ta"  +  "b")  ==  "tab"  )  

…  

How  to  test  string  concatenaZon  ?  

assert(  ("ta"  +  "a")  ==  "taa"  )  assert(  ("ta"  +  "b")  ==  "tab"  )  

assert(  ("ta"  +  "z")  ==  "taz"  )  

…  

How  to  test  string  concatenaZon  ?  

assert(  ("ta"  +  "a")  ==  "taa"  )  assert(  ("ta"  +  "b")  ==  "tab"  )  

assert(  ("ta"  +  "z")  ==  "taz"  )  

…  

But  boring  

∀n∈N,  ∃!k∈N  (n  =  2k  ⋁  n  =  2k+1)  

Remember  math  class?  

∀n,  n  =  42  

val  n:  Int  val  k:  Int    (n  ==  2k  ||  n  ==  2k  +  1)  ==  true  (n  %  2    ==  0  ||  n  %  2  ==  1)  ==  true    

In  code  

val  a:  String  val  b:  String    ((a+b)  endsWith  b)    ==  true  ((a+b)  startsWith  a)    ==  true    (a+b).length  ==  a.length  +  b.length  

String  concatenaZon  properZes  

List[Int]  =>  isPalindrome(list)    (list.reverse  ==  list)  ==>  isPalindrome(list)    (list.reverse  !=  list)  ==>  !isPalindrome(list)  

Limits  

Behavior  tesZng        Every  day  

MutaZon  tesZng        CriZcal  code  

Property  tesZng    à    CriZcal  code  

Share  and  Enjoy  

So  Long,  and  Thanks  for  All  

the  Fish  Rémy-­‐Christophe  Schermesser  

@el_picador  

Rémy-­‐Christophe  Schermesser  @el_picador  

Behavior  tesZng  Rspec  (ruby)  Jasmine  (javascript)    

MutaZon  tesZng  Javalanche  (java)  Mutant  (ruby)  

Property  tesZng  ScalaCheck  (scala)  QuickCheck  (haskell)  MrProper  (ruby)