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
Evan ShueyEvan Shuey 

Test class for contentDocument

Hello all. I am making a class that ensures that there is an attachment on an Opp whenever its closed won. Below is my class that actually does the logic. Below that is the test class. The logic works fine but I can only get 42% coverage though. The underlined code is what is not being covered, this is in a Lightning instance fyi. Thanks for the help!

public class OpportunityHandler {
    public static void checkOppForAttachment (List<Opportunity> oppList){
        
        for(Opportunity opp : oppList){
            if(opp.stageName == 'Closed Won' || opp.stageName == 'Closed Won (R)'){
                ContentDocumentLink cdl = null;
                List<ContentDocumentLink> cdl2 = [Select Id from ContentDocumentLink where LinkedEntityId =:opp.Id];
                   
                if (cdl2.isEmpty()){

                    opp.addError('Please ensure the contract is attached prior to closing this Opportunity');
                }
            }
        }
    }
}

Test Class
@isTest
public class OpportunityHandlerTest {
    @isTest public static void validateAttachment() {
        //Create Account and Opportunity
    List<RecordType> acctRecType = [Select Id from RecordType where developerName = 'Company' and sObjectType = 'Account'];
    List<RecordType> oppRecType  = [Select Id from RecordType where developerName = 'AM_Channel' and sObjectType = 'Opportunity'];
            Test.startTest();
        try{
    Account acct      = new Account();
    acct.name         = 'Test Account1';
    acct.RecordTypeId = acctRecType.get(0).id;
    insert acct;
        
    Opportunity opp     = new Opportunity();
        opp.Name        = 'test opp';
        opp.CloseDate   = system.today()+1;
        opp.StageName   = 'Initial Pitch/Demo';
        opp.Use_Case__c = 'Brand Tracker';
        opp.AccountId   = acct.Id;
        opp.RecordTypeId = oppRecType.get(0).id;
        insert opp;
        
    Opportunity opp2     = new Opportunity();
        opp2.Name        = 'test opp2';
        opp2.CloseDate   = system.today()+1;
        opp2.StageName   = 'Initial Pitch/Demo';
        opp2.Use_Case__c = 'Brand Tracker';
        opp2.AccountId   = acct.Id;
        opp2.RecordTypeId = oppRecType.get(0).id;
        insert opp2;
       

    ContentVersion contentVersion = new ContentVersion(
          Title          = 'a picture',
          PathOnClient   = 'Pic.jpg',
          VersionData    = Blob.valueOf('Test Content'),
          IsMajorVersion = true);
        insert contentVersion; 
        
List<ContentDocument> documents = [SELECT Id, Title, LatestPublishedVersionId FROM ContentDocument];

//create ContentDocumentLink  record 
    ContentDocumentLink cdl = New ContentDocumentLink();
        cdl.LinkedEntityId = opp.id;
        cdl.ContentDocumentId = documents[0].Id;
        cdl.shareType = 'V';
        cdl.visibility = 'AllUsers';
        insert cdl;
        
List<Opportunity> updateList = new List<Opportunity>();

List<ContentDocumentLink> cdl2 = [Select Id from ContentDocumentLink where LinkedEntityId =:opp.Id];
            List<Opportunity> oldOpp = [Select id from Opportunity where Name = 'test opp'];
                for (Opportunity runUpdate : oldOpp){
                    runUpdate.stageName = 'Closed Won (R)';
                    updateList.add(runUpdate);
                    system.debug(runUpdate.stageName);
                    update updateList;
                    System.assertEquals(documents.size(), 1);
                }
             }
        catch(Exception e){
            System.Assert(e.getMessage().contains('Please ensure the contract is attached prior to closing this Opportunity'));
        }
            test.stopTest();
    }

}
Best Answer chosen by Evan Shuey
Andrew GAndrew G
First, this class is not best practice - there is the old SOQL inside a FOR Loop.
Adjust that class to something as follows: note: code not compiled
public class OpportunityHandler {
    public static void checkOppForAttachment (List<Opportunity> oppList){
        list<Id> oppIds = new List<Id>();     
        for(Opportunity opp : oppList){
            if(opp.stageName == 'Closed Won' || opp.stageName == 'Closed Won (R)'){
                oppIds.add(opp.Id);
            }
        }
        ContentDocumentLink cdl = null;
        List<ContentDocumentLink> cdl2 = [Select Id from ContentDocumentLink where LinkedEntityId IN :oppIds];
        if (cdl2.isEmpty()){
            opp.addError('Please ensure the contract is attached prior to closing this Opportunity');
        } else if(cdl2.size()<>trigger.new.size()){
            opp.addError('There is a mismatch in attachments and opportunities');
        }
    }
}
As for the test class:
@IsTest
public class OpportunityHandlerTest {

    @IsTest public static void validateAttachment() {

        //Create Account and Opportunity
        List<RecordType> acctRecType = [SELECT Id FROM RecordType WHERE DeveloperName = 'Company' AND SobjectType = 'Account'];
        List<RecordType> oppRecType  = [SELECT Id FROM RecordType WHERE DeveloperName = 'AM_Channel' AND SobjectType = 'Opportunity'];
//        Test.startTest();  //why use "startTest" here?  we are only inserting test data
//        try{  //why are we doing a try in a test class for an object which is not related to the real code i want to test
            Account acct      = new Account();
            acct.Name         = 'Test Account1';
            acct.RecordTypeId = acctRecType.get(0).Id;
            insert acct;

            Opportunity opp     = new Opportunity();
            opp.Name        = 'test opp';
            opp.CloseDate   = System.today()+1;
            opp.StageName   = 'Initial Pitch/Demo';
            opp.Use_Case__c = 'Brand Tracker';
            opp.AccountId   = acct.Id;
            opp.RecordTypeId = oppRecType.get(0).Id;
            insert opp;

            ContentVersion contentVersion = new ContentVersion(
                    Title          = 'a picture',
                    PathOnClient   = 'Pic.jpg',
                    VersionData    = Blob.valueOf('Test Content'),
                    IsMajorVersion = true);
            insert contentVersion;

            List<ContentDocument> documents = [SELECT Id, Title, LatestPublishedVersionId FROM ContentDocument];

//create ContentDocumentLink  record
            ContentDocumentLink cdl = new ContentDocumentLink();
            cdl.LinkedEntityId = opp.Id;
            cdl.ContentDocumentId = documents[0].Id;
            cdl.ShareType = 'V';
            cdl.Visibility = 'AllUsers';
            insert cdl;
//if we are to do multiple tests, i would do all the test data above into a @TestSetup

            List<Opportunity> updateList = new List<Opportunity>();

            List<ContentDocumentLink> cdl2 = [SELECT Id FROM ContentDocumentLink WHERE LinkedEntityId =:opp.Id];
        //start the test when we are serious.
        Test.startTest();
            List<Opportunity> oldOpp = [SELECT Id FROM Opportunity WHERE Name = 'test opp'];
            for (Opportunity runUpdate : oldOpp){
                runUpdate.StageName = 'Closed Won (R)';
                updateList.add(runUpdate);
                System.debug(runUpdate.StageName);
                //update updateList;  //DML inside a for loop // NO
                System.assertEquals(documents.size(), 1);
                //this assert does nothing
                // except prove that we got a document when we queried the document object earlier
                // - it has no value in asserting our code except to say we got a document when using SOQL
            }
        //if we want a try catch, do it here whilst we are actually doing a DML for an update
        Boolean exceptionThrown = false;
        try {
            update updateList;
            //at this point i would also do a positive/negative test assert
        }
        catch(Exception e){
            System.debug(e.getMessage());
            exceptionThrown = true;

            // assert
            Boolean expectedExceptionThrown =  (e.getMessage().contains('Please ensure the contract is attached prior to closing this Opportunity')) ? true : false;
            System.AssertEquals(true, expectedExceptionThrown, e.getMessage());
        }
        Test.stopTest();
    }

}

give that a whirl

Regards

Andrew