function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
rraischrraisch 

Quick test coverage question

Hi.

 

Several triggers I've written call class functions that wrap database operations with the goal that calling functions can perform a database rollback if a wrapped operation fails.

 

Since this is my first real attempt at APEX coding and after some head scratching, I've gotten everything covered except two lines I'm not sure how or even if I can test.

 

For example, given a simple wrapper class:

 

 

public class PackageHelper { public class PackageHelperException extends Exception {} public static Boolean addAccountNote(Account a, String title, String body) { if( null == a) throw new PackageHelperException('####addAccountNote: a == null'); System.debug('#################### >> addAccountNote'); title = (null == title || title.length() < 1) ? 'No title provided' : title; body = (null == body || body.length() < 1) ? 'No body provided' : body; try { insert new Note( ParentId = a.Id, Title = title, Body = body); } catch ( System.DmlException e) { System.debug('#################### addAccountNote ' +'could not insert note:'+e.getMessage()); return false; } return true; } // test for addAccountNote private static testMethod void test_addAccountNote() { String title = 'Note Title'; String body = 'Note Body';

Account a;

Note t;

Boolean result;

try { result = addAccountNote((Account)null, title, body); } catch (Exception e) { System.assert(e.getMessage().contains('a == null')); }

System.assertEquals(null, result);

a = new Account(Name='Test Account'); insert a;

 

// add a note

System.assertEquals(true, addAccountNote(a, (String)null, ''));

// was note added?

t = [ select Id, Title, Body from Note where ParentId = :a.Id limit 1 ]; System.assertEquals('No title provided', t.title); System.assertEquals('No body provided', t.body);

 

// ok, delete it delete t;

 

// add another System.assertEquals(true, addAccountNote(a, '', (String)null)); t = [ select Id, Title, Body from Note where ParentId = :a.Id limit 1 ]; System.assertEquals('No title provided', t.title); System.assertEquals('No body provided', t.body); delete t;

 

// and another System.assertEquals(true, addAccountNote(a, title, body)); t = [ select Id, Title, Body from Note where ParentId = :a.Id limit 1 ]; System.assertEquals(title, t.title); System.assertEquals(body, t.body); delete a; } }

 

 

Running the tests on this class reports full coverage except for these two highlighted lines:

 

 

try {
insert new Note( ParentId = a.Id, Title = title, Body = body);
}
catch ( System.DmlException e) {
System.debug('#################### addAccountNote '
+'could not insert note:'+e.getMessage());
return false;
}

 

Perhaps I'm missing something fundamental, but how can I test for an exception which, in the best of all worlds, never occurs and over which I have no control?

 

Thanks.

 

 

JeremyKraybillJeremyKraybill

In most cases, you can just not cover these -- remember you only need 75% coverage overall. If you really need/want to test an exception block that you can't hit by normal means, sometimes you have to do something like:

 

 

Boolean testMode = false; public PageReference getBlah() { try { [some code here] if (testMode) throw new SomeTypeOfException(); } catch (Exception e) { [more code here] } } static testMethod void myTest() { MyController ctl = new MyController(); ctl.testMode = true; ctl.getBlah(); System.assert(something); }

HTH

 

Jeremy Kraybill

Austin, TX

 

 

rraischrraisch

Jeremy,

 

Thanks for your advice.

 

After creating a static TestState class

 

public static class TestState { public static boolean TESTING = false; public static void start(); public static void stop(); public static boolean running(); }

adding a testing failure to my methods, as in:

 

public static void doSomething() { if(TestState.running()) insert new Contact(); // throws DmlException ... }

 

 adding new tests using TestState bracketing

 

TestState.start(); try { doSomething(); // should throw a DmlException throw new TestException('failed'); } catch (DmlException e) { System.assert(true); } TestState.stop();

 

and refactoring interior methods so they bubble exceptions up, rather than relying on return codes, I've increased coverage to 90%.

 

And yes as far as Force.com is concerned, 90% is more than sufficient to install my package but when I started developing in Apex, one of my goals was to understand its testing requirements and leaving that last 10% uncovered means I don't really understand. ;-)

 

So, here's the remaining issue:

 

I use the following idiom in several top-level methods (uncovered code highlighted in red):

 

 

Contact c = [ ... ];


Account a = [ ... ];

try {
doSomething(a);
}
catch (Exception e) {
System.debug('#################### doAfterContactInsert'
+' failed to doSomething:'+e.getMessage()
+', rolling back database');
Database.rollback(sp);
}
finally {

try {
doSomethingElse(a, c); // dependant on success of previous function
}
catch (Exception e) {
System.debug('#################### doAfterContactInsert'
+' failed to doSomethingElse:'+e.getMessage()
+', rolling back database');
Database.rollback(sp);
}
}

 

After I adding the lines in blue so I could test for the failures they generate:

 

 

Contact c = [ ... ];

Account a = [ ... ];

try {
doSomething(a);
if(TestState.running()) insert new Contact();
}
catch (Exception e) {
System.debug('#################### doAfterContactInsert'
+' failed to doSomething:'+e.getMessage()
+', rolling back database');
Database.rollback(sp);
}
finally {

try {
doSomethingElse(a, c); // dependant on success of previous function
if(TestState.running()) insert new Contact();
}
catch (Exception e) {
System.debug('#################### doAfterContactInsert'
+' failed to doSomethingElse:'+e.getMessage()
+', rolling back database');
Database.rollback(sp);
}
}

 

I find that, rather than reducing the number of uncovered lines, the lines in blue were also added as uncovered.

 

Any ideas?

 

Thanks again.