1

I have a simple helper method in my Apex Code that loads an external ID from a field on the related Case.Contact. I have problems to write a proper unit test that covers the last three lines, including the if(…) and the return. I have seen this general answer, unfortunately it does not help me. This is the method

@TestVisible 
private static String getCoursePortalId(String recId) {
    if(String.isBlank(recId))
        return null;
    //Decimal coursePortalIdDecimal = [SELECT Contact.Course_Portal_ID__c FROM Case WHERE Id = :recId].Contact.Course_Portal_ID__c;
    Case caseObj = [SELECT Contact.Course_Portal_ID__c FROM Case WHERE Id = :recId];
    if(caseObj == null || caseObj.Contact == null || caseObj.Contact.Course_Portal_ID__c == null) 
        return null;
    return caseObj.Contact.Course_Portal_ID__c.toPlainString(); //Decimal to String
}

And this is the corresponding test attempt:

public static testMethod void testGetCoursePortalId() {

    // Create case with all required fields
    Account accountObj = new Account(
        Name = 'JohnDoeAccount'
    );
    insert accountObj;
    Contact contactObj = new Contact(
        FirstName = 'John',
        LastName = 'Doe',
        Course_Portal_ID__c = 1,
        AccountId = accountObj.Id
    );
    insert contactObj;
    Case caseObj = new Case(
        ContactId = contactObj.Id,
        AccountId = accountObj.Id,
        Status = 'Working',
        Origin = 'Phone');        
    insert caseObj;
    caseObj = [SELECT id, Contact.Course_Portal_ID__c FROM Case WHERE id = :caseObj.id];
    System.debug('Course_Portal_ID__c: ' + caseObj.Contact.Course_Portal_ID__c);
    System.assertEquals(1, caseObj.Contact.Course_Portal_ID__c);
    System.assertEquals('1', caseObj.Contact.Course_Portal_ID__c.toPlainString());
    delete caseObj;
    delete contactObj;
    delete accountObj;

    // Create case with all required fields
    Account accountObj2 = new Account(
        Name = 'JohnDoeAccount'
    );
    insert accountObj2;
    Contact contactObj2 = new Contact(
        FirstName = 'John',
        LastName = 'Doe',
        Course_Portal_ID__c = null,
        AccountId = accountObj2.Id
    );
    insert contactObj2;
    Case caseObj2 = new Case(
        ContactId = contactObj2.Id,
        AccountId = accountObj2.Id,
        Status = 'Working',
        Origin = 'Phone');        
    insert caseObj2;
    caseObj2 = [SELECT id, Contact.Course_Portal_ID__c FROM Case WHERE id = :caseObj2.id];
    System.debug('Course_Portal_ID__c2: ' + caseObj2.Contact.Course_Portal_ID__c);
    System.assertEquals(null, caseObj2.Contact.Course_Portal_ID__c);
    delete caseObj2;
    delete contactObj2;
    delete accountObj2;

    Test.startTest();
    String moodleIdNull = MoodleApiLoaderController.getCoursePortalId(null); //also test ''
    String moodleIdExists = MoodleApiLoaderController.getCoursePortalId('1001i000005Qd6dAAC'); //SOQL does not work in tests like normal...?
    String moodleIdMissing = MoodleApiLoaderController.getCoursePortalId('1001F00000T2sjSQAR'); //SOQL does not work in tests like normal...?
    Test.stopTest();
    System.assertEquals(null, moodleIdNull);        
    System.assertEquals('4776', moodleIdExists); //JZ ID:23391        
    System.assertEquals(null, moodleIdMissing); //JZ ID:
}

The dummy data does not help (removing it changes nothing but I kept it to show what I've tried.).
How can I test lines like that?

  • Your code looks like it should work.... what errors are you encountering? – Sebastian Kessel Apr 6 at 16:16
  • I would like to select both answers as accepted, thank you again, however, I selected the more general answer. – wp78de Apr 6 at 17:15
3

You're most of the way there, and I think you have all of the code you need, but there's a couple of key misconceptions here that are holding you back. Let's start here:

Test.startTest();
String moodleIdNull = MoodleApiLoaderController.getCoursePortalId(null); //also test ''
String moodleIdExists = MoodleApiLoaderController.getCoursePortalId('1001i000005Qd6dAAC'); //SOQL does not work in tests like normal...?
String moodleIdMissing = MoodleApiLoaderController.getCoursePortalId('1001F00000T2sjSQAR'); //SOQL does not work in tests like normal...?
Test.stopTest();

Your unit tests can't see those records. Unit tests execute in an isolated data environment and must create their own data that matches the specific entry conditions for the logical code path that they want to test.

But you're actually already doing that. You're just not using the data. You have two sequences of data creation, one where you build "happy path" data with a real Course Portal Id populated, and one where you have a null Course Portal Id. But instead of using those records by passing the appropriate Case Id into MoodleApiLoaderController.getCoursePortalId(), you just delete them.

Instead, break this out into a couple of different unit tests (separate logic paths should be tested separately). In one, use your happy path data. Call MoodleApiLoaderController.getCoursePortalId() with the Case Id, and write an assertion to verify that you get back the expected Course Portal Id.

In the second, use your null Course Id data. Again, callMoodleApiLoaderController.getCoursePortalId() with the created Case, and write an assertion to verify that you get back null.

That will get you full coverage, and best-practice unit tests.

Ed: And you should also apply all of the great suggestions in Sebastian's answer.

| improve this answer | |
  • Jinx again, David! – Sebastian Kessel Apr 6 at 16:22
  • Thank you for taking the time writing this up and setting things straight! – wp78de Apr 6 at 17:08
2

First, I would recommend that If you are passing an Id to a method, you use the Id type, not the String type. :)

Second, never pass a hardcoded Id to a method... that's just asking for trouble.

Third, you may want to either try-catch the select query or to cast into a List in case the Id does not exist. It is significantly safer.

Having said that, here is a snippet that should work:

Apex (last 3 lines)

List<Case> caseObjs = [SELECT Contact.Course_Portal_ID__c FROM Case WHERE Id = :recId];

if(caseObjs.isEmpty() || caseObjs[0].Contact == null || caseObjs[0].Contact.Course_Portal_ID__c == null) {
    return null;
}

return caseObjs[0].Contact.Course_Portal_ID__c.toPlainString(); //Decimal to String

Test (only relevant lines changed)

First, remove all three "delete" lines (caseObj2, accountObj2 and contactObj2). Not sure why you're doing it, but you don't have to get rid of records in test classes.

Test.startTest();
String moodleIdNull = MoodleApiLoaderController.getCoursePortalId(null); //also test ''
String moodleIdExists = MoodleApiLoaderController.getCoursePortalId(caseObj2.Id); 
String moodleIdMissing = MoodleApiLoaderController.getCoursePortalId(accountObj2.Id); //Any Id that does not exist in the case object should work here 
Test.stopTest();
| improve this answer | |
  • 1
    Thank you for all the great tips and corrections! – wp78de Apr 6 at 17:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.