+ Start a Discussion
Saad Ansari 13Saad Ansari 13 

We copied the InboundSocialPostHandlerImpl class from the Social Customer Service guide and also its corresponding test class. We had not made any modification to either of them but still when we run a test it keeps it 0% coverage.

We copied the InboundSocialPostHandlerImpl class from the Social Customer Service guide and also its corresponding test class. We had not made any modification to either of them but still when we run a test it keeps it 0% coverage.


My main question is if we are modifying the code do we really need to extend the class? What code be coming in the way of coverage if we really need to deploy?
Best Answer chosen by Saad Ansari 13
Saad Ansari 13Saad Ansari 13
Yes we were able to figure this out. basically InboundSocialPostHandlerImpl is out of the box and already exists in the system. You will need extend it however as below

global class MyInboundSocialPostHandlerImpl implements Social.InboundSocialPostHandler {}

One big caveat is, you will need to copy all functions of InboundSocialPostHandlerImpl even if you do not want to override them becuase once you start using MyInboundSocialPostHandlerImpl, the InboundSocialPostHandlerImpl class will become irrelevant (unless you chose to revert to default flows at some later stage). Make the MyInboundSocialPostHandlerImpl with your logic and write test class for all methods of MyInboundSocialPostHandlerImpl, this is simpler because of method you did not overwrite is available in InboundSocialPostHandlerImplTest (just copy paste).

Let me know if this helps

All Answers

Saad Ansari 13Saad Ansari 13
Actual class:
 
global virtual class InboundSocialPostHandlerImpl implements Social.InboundSocialPostHandler {

    final static Integer CONTENT_MAX_LENGTH = 32000;
    Boolean isNewCaseCreated = false;
    
    // Reopen case if it has not been closed for more than this number
    global virtual Integer getMaxNumberOfDaysClosedToReopenCase() {
        return 5;
    }
    
    // Create a case if one of these post tags are on the SocialPost, regardless of the skipCreateCase indicator.
    global virtual Set<String> getPostTagsThatCreateCase(){
        return new Set<String>();
    }

    global virtual String getDefaultAccountId() {
        return null;
    }

    global Social.InboundSocialPostResult handleInboundSocialPost(SocialPost post, SocialPersona persona, Map<String, Object> rawData) {
        Social.InboundSocialPostResult result = new Social.InboundSocialPostResult();
        result.setSuccess(true);
        matchPost(post);
        matchPersona(persona);

        if ((post.Content != null) && (post.Content.length() > CONTENT_MAX_LENGTH)) {
            post.Content = post.Content.abbreviate(CONTENT_MAX_LENGTH);
        }

        if (post.Id != null) {
            handleExistingPost(post, persona);
            return result;
        }

        setReplyTo(post, persona);
        buildPersona(persona);
        Case parentCase = buildParentCase(post, persona, rawData);
        setRelationshipsOnPost(post, persona, parentCase);
        setModeration(post, rawData);
        
        upsert post;
        
        if(isNewCaseCreated){
            updateCaseSource(post, parentCase);
        }
        
        return result;
    }
    
    private void setModeration(SocialPost post, Map<String, Object> rawData){
        //if we don't automatically create a case, we should flag the post as requiring moderator review.
        if(post.parentId == null && !isUnsentParent(rawData))
            post.reviewedStatus = 'Needed';
    }
    
    private void updateCaseSource(SocialPost post, Case parentCase){
        if(parentCase != null) {
            parentCase.SourceId = post.Id;
            //update as a new sobject to prevent undoing any changes done by insert triggers
            update new Case(Id = parentCase.Id, SourceId = parentCase.SourceId);
        }
    
    }
    
    private void handleExistingPost(SocialPost post, SocialPersona persona) {
        update post;
        if (persona.id != null)
            updatePersona(persona);
    }

    private void setReplyTo(SocialPost post, SocialPersona persona) {
        SocialPost replyTo = findReplyTo(post, persona);
        if(replyTo.id != null) {
            post.replyToId = replyTo.id;
            post.replyTo = replyTo;
        }
    }

    private SocialPersona buildPersona(SocialPersona persona) {
        if (persona.Id == null)
            createPersona(persona);
        else
            updatePersona(persona);
            
        return persona;
    }
    
    private void updatePersona(SocialPersona persona) {
        try{
            update persona;
        }catch(Exception e) {
            System.debug('Error updating social persona: ' + e.getMessage());
        }
    }
    
    private Case buildParentCase(SocialPost post, SocialPersona persona, Map<String, Object> rawData){
        if(!isUnsentParent(rawData)) {
            Case parentCase = findParentCase(post, persona);
            if (parentCase != null) {
                if (!parentCase.IsClosed) {
                    return parentCase;
                }
                else if (caseShouldBeReopened(parentCase)) {
                    reopenCase(parentCase);
                    return parentCase;
                }
            }
            if(shouldCreateCase(post, rawData)){
                isNewCaseCreated = true;
                return createCase(post, persona);
            }
        }
        
        return null;
    }
    
    private boolean caseShouldBeReopened(Case c){
        return c.id != null && c.isClosed && System.now() < c.closedDate.addDays(getMaxNumberOfDaysClosedToReopenCase());
    }

    private void setRelationshipsOnPost(SocialPost postToUpdate, SocialPersona persona, Case parentCase) {
        if (persona.Id != null) {
            postToUpdate.PersonaId = persona.Id;
            
            if(persona.ParentId.getSObjectType() != SocialPost.sObjectType) {
                postToUpdate.WhoId = persona.ParentId;
            }
        }
        if(parentCase != null) {
            postToUpdate.ParentId = parentCase.Id;
        }
    }
    
    private Case createCase(SocialPost post, SocialPersona persona) {
        Case newCase = new Case(subject = post.Name);
        if (persona != null && persona.ParentId != null) {
            if (persona.ParentId.getSObjectType() == Contact.sObjectType) {
                newCase.ContactId = persona.ParentId;
            } else if (persona.ParentId.getSObjectType() == Account.sObjectType) {
                newCase.AccountId = persona.ParentId;
            }
        }
        if (post != null && post.Provider != null) {
            newCase.Origin = post.Provider;
        }
        insert newCase;
        return newCase;
    }

    private Case findParentCase(SocialPost post, SocialPersona persona) {
        Case parentCase = null;
        if (post.ReplyTo != null && !isReplyingToAnotherCustomer(post, persona) && !isChat(post)) {
            parentCase = findParentCaseFromPostReply(post);
        }
        if (parentCase == null) {
            parentCase = findParentCaseFromPersona(post, persona);
        }
        return parentCase;
    }
    
    private boolean isReplyingToAnotherCustomer(SocialPost post, SocialPersona persona){
        return !post.ReplyTo.IsOutbound && post.ReplyTo.PersonaId != persona.Id;
    }
    
    private boolean isChat(SocialPost post){
        return post.messageType == 'Private' || post.messageType == 'Direct';
    }

    private Case findParentCaseFromPostReply(SocialPost post) {
        if (post.ReplyTo != null && String.isNotBlank(post.ReplyTo.ParentId)) {
            List<Case> cases = [SELECT Id, IsClosed, Status, ClosedDate FROM Case WHERE Id = :post.ReplyTo.ParentId LIMIT 1];
            if(!cases.isEmpty()) {
                return cases[0];
            }
        }
        return null;
    }
    
    private Case findParentCaseFromPersona(SocialPost post, SocialPersona persona) {
        SocialPost lastestInboundPostWithSamePersonaAndRecipient = findLatestInboundPostBasedOnPersonaAndRecipient(post, persona);
        if (lastestInboundPostWithSamePersonaAndRecipient != null) {
            List<Case> cases = [SELECT Id, IsClosed, Status, ClosedDate FROM Case WHERE id = :lastestInboundPostWithSamePersonaAndRecipient.parentId LIMIT 1];
            if(!cases.isEmpty()) {
                return cases[0];
            }
        }
        return null;
    }

    private void reopenCase(Case parentCase) {
        SObject[] status = [SELECT MasterLabel FROM CaseStatus WHERE IsClosed = false AND IsDefault = true];
        parentCase.Status = ((CaseStatus)status[0]).MasterLabel;
        update parentCase;
    }

    private void matchPost(SocialPost post) {
            if (post.Id != null) return;
        
        performR6PostIdCheck(post);
        
        if (post.Id == null){
            performExternalPostIdCheck(post);
        }
    }
    
    
    private void performR6PostIdCheck(SocialPost post){
        if(post.R6PostId == null) return;
        List<SocialPost> postList = [SELECT Id FROM SocialPost WHERE R6PostId = :post.R6PostId LIMIT 1];
        if (!postList.isEmpty()) {
            post.Id = postList[0].Id;
        }
    }
    
    
    private void performExternalPostIdCheck(SocialPost post) {
        if (post.provider == 'Facebook' && post.messageType == 'Private') return;
        if (post.provider == null || post.externalPostId == null) return;
        List<SocialPost> postList = [SELECT Id FROM SocialPost WHERE ExternalPostId = :post.ExternalPostId AND Provider = :post.provider LIMIT 1];
        if (!postList.isEmpty()) {
            post.Id = postList[0].Id;
        }
    }
    
    
    private SocialPost findReplyTo(SocialPost post, SocialPersona persona) {
        if(post.replyToId != null && post.replyTo == null)
            return findReplyToBasedOnReplyToId(post);
        if(post.responseContextExternalId != null){
            if((post.provider == 'Facebook' && post.messageType == 'Private') || (post.provider == 'Twitter' && post.messageType == 'Direct')){
                SocialPost replyTo = findReplyToBasedOnResponseContextExternalPostIdAndProvider(post);
                if(replyTo.id != null) 
                    return replyTo;
            }
            return findReplyToBasedOnExternalPostIdAndProvider(post);
        }
        return new SocialPost();
    }

    private SocialPost findReplyToBasedOnReplyToId(SocialPost post){
        List<SocialPost> posts = [SELECT Id, ParentId, IsOutbound, PersonaId FROM SocialPost WHERE id = :post.replyToId LIMIT 1];
        if(posts.isEmpty())
            return new SocialPost();
        return posts[0];
    }

    private SocialPost findReplyToBasedOnExternalPostIdAndProvider(SocialPost post){
        List<SocialPost> posts = [SELECT Id, ParentId, IsOutbound, PersonaId FROM SocialPost WHERE Provider = :post.provider AND ExternalPostId = :post.responseContextExternalId LIMIT 1];
        if(posts.isEmpty())
            return new SocialPost();
        return posts[0];
    }
    
    private SocialPost findReplyToBasedOnResponseContextExternalPostIdAndProvider(SocialPost post){
        List<SocialPost> posts = [SELECT Id, ParentId, IsOutbound, PersonaId FROM SocialPost WHERE Provider = :post.provider AND responseContextExternalId = :post.responseContextExternalId ORDER BY posted DESC NULLS LAST LIMIT 1];
        if(posts.isEmpty())
            return new SocialPost();
        return posts[0];
    }

    private SocialPost findLatestInboundPostBasedOnPersonaAndRecipient(SocialPost post, SocialPersona persona) {
        if (persona != null && String.isNotBlank(persona.Id) && post != null && String.isNotBlank(post.Recipient)) {
            List<SocialPost> posts = [SELECT Id, ParentId FROM SocialPost WHERE Provider = :post.provider AND Recipient = :post.Recipient AND PersonaId = :persona.id AND IsOutbound = false ORDER BY CreatedDate DESC LIMIT 1];
            if (!posts.isEmpty()) {
                return posts[0];
            }
        }
        return null;
    }

    private void matchPersona(SocialPersona persona) {
        if (persona != null) {
            List<SocialPersona> personaList = new List<SocialPersona>();
            if(persona.Provider != 'Other' && String.isNotBlank(persona.ExternalId)) {
                personaList = [SELECT Id, ParentId FROM SocialPersona WHERE
                    Provider = :persona.Provider AND
                    ExternalId = :persona.ExternalId LIMIT 1];
            } else if(persona.Provider == 'Other' && String.isNotBlank(persona.ExternalId) && String.isNotBlank(persona.MediaProvider)) {
                personaList = [SELECT Id, ParentId FROM SocialPersona WHERE
                    MediaProvider = :persona.MediaProvider AND
                    ExternalId = :persona.ExternalId LIMIT 1];
            } else if(persona.Provider == 'Other' && String.isNotBlank(persona.Name) && String.isNotBlank(persona.MediaProvider)) {
                personaList = [SELECT Id, ParentId FROM SocialPersona WHERE
                    MediaProvider = :persona.MediaProvider AND
                    Name = :persona.Name LIMIT 1];
            }
                    
            if (!personaList.isEmpty()) {
                persona.Id = personaList[0].Id;
                persona.ParentId = personaList[0].ParentId;
            }
        }
    }

    private void createPersona(SocialPersona persona) {
        if (persona == null || String.isNotBlank(persona.Id) || !isThereEnoughInformationToCreatePersona(persona))
            return;

        SObject parent = createPersonaParent(persona);
        persona.ParentId = parent.Id;
        insert persona;
    }

    private boolean isThereEnoughInformationToCreatePersona(SocialPersona persona) {
        return String.isNotBlank(persona.Name) && 
               String.isNotBlank(persona.Provider) && 
               String.isNotBlank(persona.MediaProvider);
    }
    
    private boolean shouldCreateCase(SocialPost post, Map<String, Object> rawData) {
        return !isUnsentParent(rawData) && (!hasSkipCreateCaseIndicator(rawData) || hasPostTagsThatCreateCase(post));
    }
    
    private boolean isUnsentParent(Map<String, Object> rawData) {
        Object unsentParent = rawData.get('unsentParent');
        return unsentParent != null && 'true'.equalsIgnoreCase(String.valueOf(unsentParent));
    }

    private boolean hasSkipCreateCaseIndicator(Map<String, Object> rawData) {
        Object skipCreateCase = rawData.get('skipCreateCase');
        return skipCreateCase != null && 'true'.equalsIgnoreCase(String.valueOf(skipCreateCase));
    }
    
    private boolean hasPostTagsThatCreateCase(SocialPost post){
        Set<String> postTags = getPostTags(post);
        postTags.retainAll(getPostTagsThatCreateCase());
        return !postTags.isEmpty();
    }
    
    private Set<String> getPostTags(SocialPost post){
        Set<String> postTags = new Set<String>();
        if(post.postTags != null)
            postTags.addAll(post.postTags.split(',', 0));
        return postTags;
    }

    global String getPersonaFirstName(SocialPersona persona) {
        String name = getPersonaName(persona);      
        String firstName = '';
        if (name.contains(' ')) {
            firstName = name.substringBeforeLast(' ');
        }
        firstName = firstName.abbreviate(40);
        return firstName;
    }
    
    global String getPersonaLastName(SocialPersona persona) {   
        String name = getPersonaName(persona);   
        String lastName = name;
        if (name.contains(' ')) {
            lastName = name.substringAfterLast(' ');
        }
        lastName = lastName.abbreviate(80);
        return lastName;
    }
    
    private String getPersonaName(SocialPersona persona) {
        String name = persona.Name.trim();
        if (String.isNotBlank(persona.RealName)) {
            name = persona.RealName.trim();
        }
        return name;
    }
    
    global virtual SObject createPersonaParent(SocialPersona persona) {

        String firstName = getPersonaFirstName(persona);
        String lastName = getPersonaLastName(persona);
        
        Contact contact = new Contact(LastName = lastName, FirstName = firstName);
        String defaultAccountId = getDefaultAccountId();
        if (defaultAccountId != null)
            contact.AccountId = defaultAccountId;
        insert contact;
        return contact;
    }


}

Test Class:
@isTest
public class InboundSocialPostHandlerImplTest {
static Map<String, Object> sampleSocialData;
static Social.InboundSocialPostHandlerImpl handler;
static {
    handler = new Social.InboundSocialPostHandlerImpl();
    sampleSocialData = getSampleSocialData('1');
}
static testMethod void verifyNewRecordCreation() {
    SocialPost post = getSocialPost(sampleSocialData);
    SocialPersona persona = getSocialPersona(sampleSocialData);
    test.startTest();
    	//handler.createPersonaParent(persona);
        handler.handleInboundSocialPost(post, persona, sampleSocialData);
    test.stopTest();
    SocialPost createdPost = [SELECT Id, PersonaId, ParentId, WhoId FROM SocialPost];
    SocialPersona createdPersona = [SELECT Id, ParentId FROM SocialPersona];
    Contact createdContact = [SELECT Id FROM Contact Limit 1];
    Case createdCase = [SELECT Id, ContactId FROM Case];
    System.assertEquals(createdPost.PersonaId, createdPersona.Id, 'Post is not linked to the Persona.');
    System.assertEquals(createdPost.WhoId, createdPersona.ParentId, 'Post is not linked to the Contact');
    System.assertEquals(createdPost.ParentId, createdCase.Id, 'Post is not linked to the Case.');
    System.assertEquals(createdCase.ContactId, createdContact.Id, 'Contact is not linked to the Case.');
}
static testMethod void matchSocialPostRecord() {
    SocialPost existingPost = getSocialPost(getSampleSocialData('2'));
    insert existingPost;
    SocialPost post = getSocialPost(sampleSocialData);
    post.R6PostId = existingPost.R6PostId;
    SocialPersona persona = getSocialPersona(sampleSocialData);
    test.startTest();
    	handler.handleInboundSocialPost(post, persona, sampleSocialData);
    test.stopTest();
    System.assertEquals(1, [SELECT Id FROM SocialPost].size(), 'There should be only 1 post');
}
static testMethod void matchSocialPersonaRecord() {
    Contact existingContact = new Contact(LastName = 'LastName');
    insert existingContact;
    SocialPersona existingPersona = getSocialPersona(getSampleSocialData('2'));
    existingPersona.ParentId = existingContact.Id;
    insert existingPersona;
    SocialPost post = getSocialPost(sampleSocialData);
    SocialPersona persona = getSocialPersona(sampleSocialData);
    persona.ExternalId = existingPersona.ExternalId;
    test.startTest();
    	handler.handleInboundSocialPost(post, persona, sampleSocialData);
    test.stopTest();
    SocialPost createdPost = [SELECT Id, PersonaId, ParentId, WhoId FROM SocialPost];
    SocialPersona createdPersona = [SELECT Id, ParentId FROM SocialPersona];
    Contact createdContact = [SELECT Id FROM Contact];
    Case createdCase = [SELECT Id, ContactId FROM Case];
    System.assertEquals(createdPost.PersonaId, createdPersona.Id, 'Post is not linked to the Persona.');
    System.assertEquals(createdPost.WhoId, createdPersona.ParentId, 'Post is not linked to the Contact');
    System.assertEquals(createdPost.ParentId, createdCase.Id, 'Post is not linked to the Case.');
    System.assertEquals(createdCase.ContactId, createdContact.Id, 'Contact is not linked to the Case.');
}
static testMethod void matchCaseRecord() {
    Contact existingContact = new Contact(LastName = 'LastName');
    insert existingContact;
    SocialPersona existingPersona = getSocialPersona(getSampleSocialData('2'));
    existingPersona.ParentId = existingContact.Id;
    insert existingPersona;
    Case existingCase = new Case(ContactId = existingContact.Id, Subject = 'Test Case');
    insert existingCase;
    SocialPost existingPost = getSocialPost(getSampleSocialData('2'));
    existingPost.ParentId = existingCase.Id;
    existingPost.WhoId = existingContact.Id;
    existingPost.PersonaId = existingPersona.Id;
    insert existingPost;
    SocialPost post = getSocialPost(sampleSocialData);
    post.responseContextExternalId = existingPost.ExternalPostId;
    test.startTest();
    handler.handleInboundSocialPost(post, existingPersona, sampleSocialData);
    test.stopTest();
    SocialPost createdPost = [SELECT Id, PersonaId, ParentId, WhoId FROM SocialPost
    WHERE R6PostId = :post.R6PostId];
    System.assertEquals(existingPersona.Id, createdPost.PersonaId, 'Post is not linked to the Persona.');
    System.assertEquals(existingContact.Id, createdPost.WhoId, 'Post is not linked to the Contact');
    System.assertEquals(existingCase.Id, createdPost.ParentId, 'Post is not linked to the Case.');
    System.assertEquals(1, [SELECT Id FROM Case].size(), 'There should only be 1 Case.');
}
static testMethod void reopenClosedCase() {
    Contact existingContact = new Contact(LastName = 'LastName');
    insert existingContact;
    SocialPersona existingPersona = getSocialPersona(getSampleSocialData('2'));
    existingPersona.ParentId = existingContact.Id;
    insert existingPersona;
    Case existingCase = new Case(ContactId = existingContact.Id, Subject = 'Test Case',
    Status = 'Closed');
    insert existingCase;
    SocialPost existingPost = getSocialPost(getSampleSocialData('2'));
    existingPost.ParentId = existingCase.Id;
    existingPost.WhoId = existingContact.Id;
    existingPost.PersonaId = existingPersona.Id;
    insert existingPost;
    SocialPost post = getSocialPost(sampleSocialData);
    post.responseContextExternalId = existingPost.ExternalPostId;
    test.startTest();
    handler.handleInboundSocialPost(post, existingPersona, sampleSocialData);
    test.stopTest();
    SocialPost createdPost = [SELECT Id, PersonaId, ParentId, WhoId FROM SocialPost
    WHERE R6PostId = :post.R6PostId];
    System.assertEquals(existingPersona.Id, createdPost.PersonaId, 'Post is not linked to the Persona.');
    System.assertEquals(existingContact.Id, createdPost.WhoId, 'Post is not linked to the Contact');
    System.assertEquals(existingCase.Id, createdPost.ParentId, 'Post is not linked to the Case.');
    System.assertEquals(1, [SELECT Id FROM Case].size(), 'There should only be 1 Case.');
    System.assertEquals(false, [SELECT Id, IsClosed FROM Case WHERE Id =
    :existingCase.Id].IsClosed, 'Case should be open.');
}
static SocialPost getSocialPost(Map<String, Object> socialData) {
    SocialPost post = new SocialPost();
    post.Name = String.valueOf(socialData.get('source'));
    post.Content = String.valueOf(socialData.get('content'));
    post.Posted = Date.valueOf(String.valueOf(socialData.get('postDate')));
    post.PostUrl = String.valueOf(socialData.get('postUrl'));
    post.Provider = String.valueOf(socialData.get('mediaProvider'));
    post.MessageType = String.valueOf(socialData.get('messageType'));
    post.ExternalPostId = String.valueOf(socialData.get('externalPostId'));
    post.R6PostId = String.valueOf(socialData.get('r6PostId'));
    return post;
}
static SocialPersona getSocialPersona(Map<String, Object> socialData) {
    SocialPersona persona = new SocialPersona();
    persona.Name = String.valueOf(socialData.get('author'));
    persona.RealName = String.valueOf(socialData.get('realName'));
    persona.Provider = String.valueOf(socialData.get('mediaProvider'));
    persona.MediaProvider = String.valueOf(socialData.get('mediaProvider'));
    persona.ExternalId = String.valueOf(socialData.get('externalUserId'));
    return persona;
}
static Map<String, Object> getSampleSocialData(String suffix) {
    Map<String, Object> socialData = new Map<String, Object>();
    socialData.put('r6PostId', 'R6PostId' + suffix);
    socialData.put('r6SourceId', 'R6SourceId' + suffix);
    socialData.put('postTags', null);
    socialData.put('externalPostId', 'ExternalPostId' + suffix);
    socialData.put('content', 'Content' + suffix);
    socialData.put('postDate', '2015-01-12T12:12:12Z');
    socialData.put('mediaType', 'Twitter');
    socialData.put('author', 'Author');
    socialData.put('skipCreateCase', false);
    socialData.put('mediaProvider', 'TWITTER');
    socialData.put('externalUserId', 'ExternalUserId');
    socialData.put('postUrl', 'PostUrl' + suffix);
    socialData.put('messageType', 'Tweet');
    socialData.put('source', 'Source' + suffix);
    socialData.put('replyToExternalPostId', null);
    socialData.put('realName', 'Real Name');
    return socialData;
}
  
}

Both classes are available in the document:
https://resources.docs.salesforce.com/sfdc/pdf/social_customer_service_impl_guide.pdf
navya m 23navya m 23
Hi,

Any update on this??? How did u deploy?? Facing same issue.Could you please help.
Saad Ansari 13Saad Ansari 13
Yes we were able to figure this out. basically InboundSocialPostHandlerImpl is out of the box and already exists in the system. You will need extend it however as below

global class MyInboundSocialPostHandlerImpl implements Social.InboundSocialPostHandler {}

One big caveat is, you will need to copy all functions of InboundSocialPostHandlerImpl even if you do not want to override them becuase once you start using MyInboundSocialPostHandlerImpl, the InboundSocialPostHandlerImpl class will become irrelevant (unless you chose to revert to default flows at some later stage). Make the MyInboundSocialPostHandlerImpl with your logic and write test class for all methods of MyInboundSocialPostHandlerImpl, this is simpler because of method you did not overwrite is available in InboundSocialPostHandlerImplTest (just copy paste).

Let me know if this helps
This was selected as the best answer