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	
  
assertThatMyTestFitsOn
OneLine(whatIExpect,	
  
whatMyCodeIsDoing);	
  
And	
  
void	
  testWithCamelCase
ToReadItBeFer()	
  {	
  ...	
  }	
  
And	
  again	
  
@Test	
  
void	
  annotaZonsAreGood
ForYourHealth()	
  {	
  ...	
  }	
  
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)	
  

The hitchhicker’s guide to unit testing

  • 1.
    The  hitchhicker’s   guide  to  unit  tes1ng   Rémy-­‐Christophe  Schermesser   @el_picador  
  • 2.
    Ruby!   Scala!   Java!   Python!   PHP!  
  • 3.
    Ruby!   Scala!   Java!   Python!   PHP!   But  don’t  forget  your  towel  
  • 4.
    Test::Unit   ScalaTest   jUnit   uniFest   PHPUnit  
  • 5.
    Test::Unit   ScalaTest   jUnit   uniFest   PHPUnit   Again  don’t  forget  your  towel  
  • 6.
  • 7.
    assertEquals   At  the  beginning,  we  had  
  • 8.
  • 9.
  • 10.
    And  again   @Test   void  annotaZonsAreGood ForYourHealth()  {  ...  }  
  • 11.
    And  again  again   void  testMyTest()  {      Obj  obj  =  new  Obj();    //  10  lignes  of  things    assertEquals(…);   }  
  • 12.
  • 13.
  • 14.
  • 15.
    JUnit   Behavior   tes1ng   with   RSpec  
  • 16.
    Problem   void  testMyTest()  {      Obj  obj  =  new  Obj();    //  10  lignes  of  things    assertEquals(…);   }  
  • 17.
    Behavior  tesZng,  don’t  test,  do  describe   Describe  what   your  program   should  do  
  • 18.
    One  test   One  (english)   sentence   Behavior  tesZng,  don’t  test,  do  describe  
  • 19.
    RSpec,  don’t  panic,   factorize  and  do  DSL  
  • 20.
    describe  CompaniesController  do        describe  "POST  create"  do              context  "when  recruiter     signed_in  with  no  company"  do                    …              end        end   end  
  • 21.
    describe  CompaniesController  do        describe  "POST  create"  do              context  "when  recruiter     signed_in  with  no  company"  do                    …              end        end   end  
  • 22.
    describe  CompaniesController  do        describe  "POST  create"  do              context  "when  recruiter     signed_in  with  no  company  an  email  is   sent"  do                    …              end        end   end  
  • 23.
    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”
  • 24.
    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”
  • 25.
    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”
  • 26.
    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”
  • 27.
    Like  a  towel,  use  it  every  day  
  • 28.
    JUnit   Muta1on   tes1ng   Behavior   tes1ng   with   Javalanche  
  • 29.
    Code  coverage   has  limits   Code:  ctrl+c   Test:  ctrl+v  
  • 30.
    Code  a  class  and  test  it  
  • 31.
  • 32.
    &&   ++   !=   >   …   ||   -­‐-­‐   ==   <   …   Mutate  it  
  • 33.
    if(a  &&  b)  {          i++;   }  else  {          i-­‐-­‐;   }   if(a || b) {     i++; } else {     i--; } if(a && b) {     i--; } else {     i--; } Mutate  it  
  • 34.
  • 35.
    mvn  test   ant  -­‐f  javalanche.xml  mutaZonTest   Kill  it  
  • 36.
    Run  tests   Green  tests   Something’s   wrong  
  • 37.
    Run  tests   Red  tests   Great  job!  
  • 38.
    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  
  • 41.
    Behavior   tes1ng   JUnit   Property   tes1ng   Muta1on   tes1ng   with   ScalaCheck  
  • 42.
    How  to  test  string   concatenaZon  ?   assert(  ("ta"  +  "a")  ==  "taa"  )  
  • 43.
    How  to  test  string   concatenaZon  ?   assert(  ("ta"  +  "a")  ==  "taa"  )   assert(  ("ta"  +  "b")  ==  "tab"  )  
  • 44.
    How  to  test  string   concatenaZon  ?   assert(  ("ta"  +  "a")  ==  "taa"  )   assert(  ("ta"  +  "b")  ==  "tab"  )   …  
  • 45.
    How  to  test  string   concatenaZon  ?   assert(  ("ta"  +  "a")  ==  "taa"  )   assert(  ("ta"  +  "b")  ==  "tab"  )   assert(  ("ta"  +  "z")  ==  "taz"  )   …  
  • 46.
    How  to  test  string   concatenaZon  ?   assert(  ("ta"  +  "a")  ==  "taa"  )   assert(  ("ta"  +  "b")  ==  "tab"  )   assert(  ("ta"  +  "z")  ==  "taz"  )   …   But  boring  
  • 47.
    ∀n∈N,  ∃!k∈N   (n  =  2k  ⋁  n  =  2k+1)   Remember  math  class?   ∀n,  n  =  42  
  • 48.
    val  n:  Int   val  k:  Int     (n  ==  2k  ||  n  ==  2k  +  1)  ==  true   (n  %  2    ==  0  ||  n  %  2  ==  1)  ==  true     In  code  
  • 49.
    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  
  • 50.
    List[Int]  =>  isPalindrome(list)     (list.reverse  ==  list)  ==>  isPalindrome(list)     (list.reverse  !=  list)  ==>  !isPalindrome(list)   Limits  
  • 52.
    Behavior  tesZng        Every  day   MutaZon  tesZng        CriZcal  code   Property  tesZng    à    CriZcal  code   Share  and  Enjoy  
  • 53.
    So  Long,  and   Thanks  for  All   the  Fish   Rémy-­‐Christophe  Schermesser   @el_picador  
  • 54.
    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)