Upgrade to Pro — share decks privately, control downloads, hide ads and more …

10 Principles of Apex Testing

10 Principles of Apex Testing

These 10 tips will help you write useful, effective unit tests for your apex code!

Kevin Poorman

May 22, 2015
Tweet

More Decks by Kevin Poorman

Other Decks in Technology

Transcript

  1. #forcewebinar Safe  Harbor Safe  harbor  statement  under  the  Private  Securities

     Litigation  Reform  Act  of  1995: This  presentation  may  contain  forward-­looking  statements  that  involve  risks,  uncertainties,  and  assumptions.  If  any  such  uncertainties  materialize  or  if   any  of  the  assumptions  proves  incorrect,  the  results  of  salesforce.com,  inc. could  differ  materially  from  the  results  expressed  or  implied  by  the  forward-­ looking  statements  we  make.  All  statements  other  than  statements  of  historical  fact  could  be  deemed  forward-­looking,  including  any  projections  of   product  or  service  availability,  subscriber  growth,  earnings,  revenues,  or  other  financial  items  and  any  statements  regarding strategies  or  plans  of   management  for  future  operations,  statements  of  belief,  any  statements  concerning  new,  planned,  or  upgraded  services  or  technology  developments   and  customer  contracts  or  use  of  our  services. The  risks  and  uncertainties  referred  to  above  include  – but  are  not  limited  to  – risks  associated  with  developing  and  deliveringnew  functionality  for  our   service,  new  products  and  services,  our  new  business  model,  our  past  operating  losses,  possible  fluctuations  in  our  operating results  and  rate  of   growth,  interruptions  or  delays  in  our  Web  hosting,  breach  of  our  security  measures,  the  outcome  of  any  litigation,  risks  associated  with  completed  and   any  possible  mergers  and  acquisitions,  the  immature  market  in  which  we  operate,  our  relatively  limited  operating  history,  our ability  to  expand,  retain,   and  motivate  our  employees  and  manage  our  growth,  new  releases  of  our  service  and  successful  customer  deployment,  our  limited history  reselling   non-­salesforce.com products,  and  utilization  and  selling  to  larger  enterprise  customers.  Further  information  on  potential  factors  that  could  affect  the   financial  results  of  salesforce.com,  inc. is  included  in  our  annual  report  on  Form  10-­K  for  the  most  recent  fiscal  year  and  in  our  quarterly  report  on   Form  10-­Q  for  the  most  recent  fiscal  quarter.  These  documents  and  others  containing  important  disclosures  are  available  on  the  SEC  Filings  section  of   the  Investor  Information  section  of  our  Web  site. Any  unreleased  services  or  features  referenced  in  this  or  other  presentations,  press  releases  or  public  statements  are  not  currently  available  and  may   not  be  delivered  on  time  or  at  all.  Customers  who  purchase  our  services  should  make  the  purchase  decisions  based  upon  features  that  are  currently   available.  Salesforce.com,  inc. assumes  no  obligation  and  does  not  intend  to  update  these  forward-­looking  statements.
  2. #forcewebinar Go  Social! Salesforce Developers +Salesforce Developers Salesforce Developers Salesforce

    Developers The  video  will  be  posted  to YouTube  &  the  webinar  recap page  (same  URL  as  registration). This  webinar  is  being  recorded! @salesforcedevs / #forcewebinar So  make  sure  to   leave  comments   on  YouTube
  3. #forcewebinar ▪ Don’t  wait  until  the  end  to  ask  your

     question!   – Technical  support  will  answer  questions  starting  now. ▪ Respect  Q&A  etiquette – Please  don’t  repeat  questions.  The  support  team  is  working   their  way  down  the  queue. ▪ Stick  around  for  live  Q&A  at  the  end – Speakers  will  tackle  more  questions  at  the  end,  time-­ allowing.  If  no  one  asks  questions,  Kevin will. ▪ Head  to  Developer  Forums – More  questions?  Visit  developer.salesforce.com/forums Have  Questions?
  4. #forcewebinar Agenda 1. Why  we  do  Testing 2. 10  principles

     of  Apex  Testing 3. Questions  and  Answers
  5. #forcewebinar Why  we  test? § Tests  provide  assurance  of  functionality

    § Tests  reduce  cost  of  change § Tests  encourage  modular,  reusable  code § Tests  help  identify  engineering   and  architectural  bugs § Tests  help  document  expected  behavior § Tests  +  Code  =  less  likely  to  produce  bugs
  6. #forcewebinar Principle  #1,  Use  Asserts § A  test  without  Assert

     methods  isn’t  a  test,  it’s  code   execution § Three  Assert  methods  built-­in – System.Assert(boolean-­expression,  ‘friendly  message’) – System.AssertEquals(expect,  actual,  ‘friendly  message’) – System.AssertNotEquals(expected,  actual,  ‘friendly  message) § Every  test  method  should  include  at  least  one  assertion
  7. #forcewebinar Principle  #1,  Use  Asserts You  can  write  your  own

     assert  methods! @isTest Public class customAssertions{ public class customAssertionException extends Exception{} Public Boolean DuplicateAccount(Account a, Account b){ // … compare accounts if(duplicate != true) { Throw new customAssertionException(‘whoa, it’s not the same’); } return true; } }
  8. #forcewebinar Principle  #2,  use  startTest and  stopTest § Test.startTest()  and

     Test.stopTest()  help  facilitate   testing. § startTest()  resets  DML,  CPU  Time  and  other  governor   limits,  ensuring  any  limits  you  hit  come  from  your  tested   code! § stopTest()  Forces  asynchronous  code  to  complete.
  9. #forcewebinar Principle  #2,  use  startTest and  stopTest § General  pattern

     is  to   – Create  your  test  data – Start  the  test – Use  that  test  data  within  your  tested  method – End  the  Test – Assert  your  code  works  as  expected
  10. #forcewebinar Principle  #3,  Positive  Tests § Write  ‘Positive  Tests’ §

    Positive  tests  test  the  expected  behavior § Lets  talk  about  multiple  expected  behaviors § Not  just  happy  path  testing
  11. #forcewebinar Principle  #3,  Positive  Tests Public class exampleCode { Public

    Integer add(Integer one, Integer two){ return one + two; } }
  12. #forcewebinar Principle  #3,  Positive  Tests @isTest private class exampleCode_Tests {

    @isTest static void test_Add_Postive() { exampleCode drWho = new exampleCode(); Test.startTest(); Integer testValue = drWho.add(5,7); Test.stopTest(); System.assertEquals(12, testValue, ‘Expected 5+7 to equal 12’); }
  13. #forcewebinar Principle  #4,  Negative  Tests § Negative  tests  prove  that

     your  code  properly  handles   exceptions  and  errors. § The  general  pattern  is  to  call  your  method  within  a   try/catch  block  in  the  test.  Since  you  expect  an   exception  you’ll  catch  it  in  the  catch  block § Less  intuitive  but  more  powerful!
  14. #forcewebinar Principle  #4,  Negative  Tests Public class exampleCode { Public

    class exampleCodeException{} Public Integer division(Integer one, Integer two){ if(two == 0) { Throw new exampleCodeException(‘Dividing by zero makes kittens cry’); } return one / two; } }
  15. #forcewebinar Principle  #4,  Negative  Tests @isTest private class exampleCode_Tests {

    @isTest static void test_Divide_Negative() { exampleCode drWho = new exampleCode(); Boolean didCatchProperException = false; Test.startTest(); Try { drWho.divide(1, 0); } catch (exampleCodeException AwesomeException){ didCatchProperException = true; } Test.stopTest(); System.assert(didCatchProperException, ‘Properly caught custom Exception’); }
  16. #forcewebinar Principle  #5,  User  Tests § User  based  tests  prove

     your  security  model § Test  with  Users  of  different  Roles  /  Profiles  and   Permission  sets! § The  pattern  works  like  this:  Create  a  user  with  a  given   profile.  As  needed  assign  permission  sets.  Test  both   positive  and  negative  users.
  17. #forcewebinar Principle  #5,  User  Tests Public class exampleCode { Public

    class exampleCodeException{} Public Integer getBankAccount(Account a){ return a.SuperSecretBankAccountNum__c; } }
  18. #forcewebinar Principle  #5,  User  Tests  Positive @isTest private class exampleCode_Tests

    { @isTest static void test_getBankAccount_Positive() { exampleCode drWho = new exampleCode(); User u = AwesomeTestLib.getUserWithProfile(‘JediProfile’); Account a = (Account)TestFactory.createSObject(new Account()); Integer result; System.runAs(u){ Test.startTest(); result = drWho.getBankAccount(a); Test.stopTest(); } System.assertNotEquals(result, null, ‘Expected this user to have access to bank #’); }
  19. #forcewebinar Principle  #5,  User  Tests  -­ Negative @isTest private class

    exampleCode_Tests { @isTest static void test_getBankAccount_UberForNope() { exampleCode drWho = new exampleCode(); User u = AwesomeTestLib.getUserWithProfile(‘SithProfile’); Account a = (Account)TestFactory.createSObject(new Account()); Integer result; System.runAs(u){ Test.startTest(); result = drWho.getBankAccount(a); Test.stopTest(); } System.assertEquals(result, null, ‘Expected Sith lords to be blocked’); }
  20. #forcewebinar Principle  #5,  User  Tests  – w/  Permission  set @isTest

    private class exampleCode_Tests { @isTest static void test_getBankAccount_W_PermSet() { exampleCode drWho = new exampleCode(); User u = AwesomeTestLib.getUserWithProfile(‘Standard User’); UtilityClass.AssignUserToPermissionSet(u, ‘canReadBankAccount’); Account a = (Account)TestFactory.createSObject(new Account()); Integer result; System.runAs(u){ Test.startTest(); result = drWho.getBankAccount(a); Test.stopTest(); } System.assertNotEquals(result, null, ‘Expected user with canReadBankAccount to read BankAccount#’); }
  21. #forcewebinar Principle  #6,  Use  your  own  data § Always  build

     your  own  test  data. § Unless  you  have  to,  never  use   @isTest(seeAllData=true) § Rember you  can  dynamically  write  assertions: – AssertEquals(AccountICreated.foo__c,  ResultAccount.foo__c,   ‘the  two  accounts  should  be  the  same!’);;
  22. #forcewebinar Principle  #6,  Use  your  own  data § General  pattern

     is  to   – Create  your  test  data – Use  that  test  data  within  your  tested  method – Assert  your  code  works  as  expected
  23. #forcewebinar Principle  #6,  Use  your  own  data § Tools  to

     make  this  faster: – TestFactory,  An  open  source  test  data  factory  from  Daniel   Hoechst.  Found  at  http://bit.ly/1c5exnV – Account  a  =  (Account)TestFactory.createSObject(new   Account());; – Opportunity  o  =  (Opportunity)TestFactory.createSObject(new   Opportunity(AccountId =  a.Id));; – Account[]  aList =  (Account[])TestFactory.createSObjectList(new   Account(),  200);;
  24. #forcewebinar Principle  #7,  Use  a  domain  specific  test  helper  lib

    § Test  Helpers  facilitate  faster  test  writing § Additionally  make  tests  easier  to  read,  by  abstracting   out  superfluous  code. § Mark  your  test  helper  as  @isTest to  ensure  it’s  never   called  by  live  code,  and  to  ensure  it’s  not  counted   against  you  in  code  coverage
  25. #forcewebinar Principle  #7,  Use  a  domain  specific  test  helper  lib

    § Candidates  for  Test  Helper  classes  include: – Creating  a  user  object  with  a  given  permission  set  assigned – Generating  complex  object  trees: • generateAccountWOppAndProducts() – Anything  you  do  more  than  once  in  a  test  suite – Make  them  public  static  methods  and  use  a  naming  convention • generate*  -­ generates  an  object • get*  -­ retrieves  an  object  via  SOQL
  26. #forcewebinar Principle  #8,  Mocking § Integration  v.  Unit  tests –

    Integration  tests  test  code  in  an  execution  context  – Ie setup   some  data  and  execute  the  code  within  the  context.   – Unit  tests  focus  on  a  single  unit  of  code,  not  necessarily  a   complete  function.   – Mocks  allow  us  to  write  true  unit  tests  by  ‘mocking’  objects. – You  get  to  setup  the  mock,  and  it’s  responses.
  27. #forcewebinar Principle  #8,  Mocking Unit  Test Calls  a   service

    Returns   ??? Traditional  integration  testing  
  28. #forcewebinar Principle  #8,  Mocking Unit  testing  with  mocks Mock Obj

    Unit   Test Returns   known   result Returns   known   result
  29. #forcewebinar Principle  #8,  Mocking § Use  mocks  to  insert  objects

     whenever  that  object  is  not   critical  to  the  test.   – For  instance,  if  you’re  testing  a  method  that  populates  an   Account  with  the  results  of  a  webservice,  mock  out  the  web   services’  result. – This  allows  you  to  test  just  what  that  method  does  to  the   account  object,  not  it’s  ability  to  talk  to  a  web  service
  30. #forcewebinar Principle  #8,  Mocking § Not  just  for  webservices –

    Mock  all  services! – Mock  anything  you  have  in  your  service  layer – Use  interfaces  and  write  the  unit  test  with  a  mock   implementation
  31. #forcewebinar Principle  #8,  Mocking @isTest Public class exampleCode_Tests{ Public Boolean

    MockExample(Account a, Account b){ fflib_apexMocks m = new ffLib_apexMocks(); myInterface mock = new MockMyService(m); m.startStubbing(); m.when(mock.add(5,7)).thenReturn(12); m.stopStubbing(); Test.startTest(); ExampleCode mockObj = new exampleCode(myAwesomeService); Integer result = mockObj.add(5,7); Test.stopTest(); System.assertEquals(12, result, ‘friendly message’); } }
  32. #forcewebinar Principle  #8,  Mocking  Part  2 § Mocking  HTTP  Responses

    § Allows  you  to  test  HTTP  Callouts  without  making  the   Callout. § Interface  requires  you  to  return  an  HTTP  Response   object.  
  33. #forcewebinar Principle  #8,  Mocking  Part  2  – Use  a  factory

    @isTest public with sharing class HTTPMockCalloutFactory implements HttpCalloutMock { HTTPMockCalloutFactory (Integer code, String status, String body, Map<String, String> responseHeaders) { // set class variables here. } public HTTPResponse respond(HTTPRequest req) { HttpResponse res = new HttpResponse(); res.setStatusCode(this.code); res.setStatus(this.status); res.setBody(this.bodyAsString); return res; } }
  34. #forcewebinar Principle  #8,  Mocking  Part  2  – Use  a  factory

    § You  can  even  extend  your  factory  to  return  different   response  based  on  a  static  class  variable.  Useful  for   testing  multiple  callout  scenarios. – Write  an  additional  constructor  that  accepts  a  list  of  json strings   that  de-­serialize  into  responses
  35. #forcewebinar Principle  #9,  Write  for  testing § Write  small,  tightly

     focused  methods  that  play  well  with   others.   § Compose  advanced  functionality  by  using  your  small   methods  together § Write  more-­or-­less  unit  tests  with  these  focused   methods,  and  integration  tests  for  customized  logic.
  36. #forcewebinar Principle  #9,  Write  for  testing:  guidelines § Keep  your

     methods  to  no  more  than  20  lines.   § Keep  method  arguments  to  a  minimum  – no  more  than   four. § Write  your  Visualforce  controllers  to  use  a  single   wrapper  object.   – Either  mock  this  wrapper  object,  or  write  a  Test  Helper  method   to  construct  it. § Use  descriptive  method  names!
  37. #forcewebinar Principle  #10,  Use  Continuous  Integration! § Continuous  Integration  is

     the  idea  that  every  code-­ change  committed  to  your  source  repository  triggers  a   full  run  of  the  tests. § A  number  of  tools  are  available  for  CI  on  the   Salesforce1  platform.   § Travis.ci,  Drone.io,  Jenkins  and  Codeship are  some   examples
  38. #forcewebinar Principle  #10,  Use  Continuous  Integration! § CI  gives  you

     insight  to  failing  tests  as  soon  as  they’re   committed  not  when  you  try  to  deploy § Facilitates  multiple  developers  working  on  the  same   project  in  their  own  sandboxes  /  dev  orgs § Keeps  you  aware  of  code  coverage  as  development   occurs.
  39. #forcewebinar Useful  tips § Don’t  insert  or  query  unless  you

     have  to.  You  can  often   test  with  objects  in  memory! § Learn  more  about  Mocking  by  watching  Jesse  Altman’s   (@JesseAltman)  Excellent  intro  to  mocking  with  Apex   mocks  here:  http://bit.ly/1HtXk2B § Write  and  use  a  standardized  Logging  library  that  wraps   log  data  in  a  highly  visible  header/footer
  40. #forcewebinar Survey Your  feedback  is  crucial  to  the  success of

     our  webinar  programs.  Thank  you! http://bit.ly/1FCyGiR
  41. #forcewebinar New  York  City June  16-­18,  2015 Join  us  in

    the  Developer  Zone Salesforce World  Tour Register here: http://sfdc.co/XXXX