mattaddy / SObjectFabricator

An SObject fabrication API to reduce database interactions and dependencies on triggers in Apex unit tests.
Other
89 stars 14 forks source link

Error sfab_FabricatedSObject.FieldIsNotParentRelationshipException: The field Subscription__c.Organisation__c is not a parent relationship #21

Closed cdonnelly-cms closed 3 years ago

cdonnelly-cms commented 3 years ago

Hi, Thank you for providing this library. It sounds like a great way to improve unit test execution times.

I'm trying to use it for the first time and I'm getting the following runtime error:

sfab_FabricatedSObject.FieldIsNotParentRelationshipException: The field Subscription__c.Organisation__c is not a parent relationship  
Class.sfab_FabricatedSObject.checkFieldIsParentRelationship: line 534, column 1                                                       
Class.sfab_FabricatedSObject.setDirectParent: line 389, column 1                                                                      
Class.sfab_FabricatedSObject.setParent: line 240, column 1                                                                            
Class.sfab_FabricatedSObject.set: line 112, column 1                                                                                  
Class.sfab_FabricatedSObject: line 192, column 1                                                                                      
Class.sfab_FabricatedSObject.<init>: line 58, column 1                                                                                
Class.COMMS_subscription_TRIGGER_mc_TEST.after_update_organisation_subscription_happy: line 84, column 1 

My code is:

    Map<string,object> publicationGeneralMap = new Map<string,object>
        { 'Id'                  => 'Id-Pub-Gen-1'
        , 'Name'                => SETUP_NAME_PUBLICATION
        , 'RecordTypeId'        => toRecordTypeId(Schema.SObjectType.Publication__c, 'General_Publication')
        , 'Branch_Ownership__c' => SETUP_BRANCH_OWNERSHIP
        , 'Status__c'           => SETUP_PUBLICATION_STATUS
        , 'Email__c'            => true
        };
    sfab_FabricatedSObject fabPubGen = new sfab_FabricatedSObject( Publication__c.class, publicationGeneralMap );

    Map<string,object> accountGeneralMap = new Map<string,object>
        { 'Id'                    => 'Id-Acc-Gen-1'
        , 'Name'                  => SETUP_NAME_GENERAL_ACCOUNT
        , 'RecordTypeId'          => toRecordTypeId(Schema.SObjectType.Account, 'General_Account')
        , 'CMS_ID__c'             => SETUP_ORGANISATION_CMS_ID
        , 'Branch_Ownership__c'   => SETUP_BRANCH_OWNERSHIP
        , 'Status__c'             => 'Active'
        , 'Organisation_Email__c' => SETUP_ORGANISATION_EMAIL
        };
    sfab_FabricatedSObject fabAccGen = new sfab_FabricatedSObject( Account.class, accountGeneralMap );

    Map<string,object> subscriptionOrganisationMap = new Map<string,object>
        { 'Id'                    => 'Id-Sub-Org-1'
        , 'Organisation__c'       => fabAccGen
        , 'Publication__c'        => fabPubGen
        , 'Email_Subscription__c' => true
        , 'Status__c'             => 'Subscribed'
        , 'Email_Preference__c'   => 'Organisation Email'
        , 'Branch_Ownership__c'   => SETUP_BRANCH_OWNERSHIP
        , 'RecordTypeId'          => toRecordTypeId(Schema.SObjectType.Subscription__c, 'Organisation_Subscription')
        };
      sfab_FabricatedSObject fabSubOrg = new sfab_FabricatedSObject( Subscription__c.class, subscriptionOrganisationMap );

It is falling over on the last line. Subscription is a custom object. The field meta data for its Organisation field is:

<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Organisation__c</fullName>
    <deleteConstraint>Restrict</deleteConstraint>
    <externalId>false</externalId>
    <inlineHelpText>If the subscription is for an Organisation, select the name of the organisation here.</inlineHelpText>
    <label>Organisation</label>
    <lookupFilter>
        <active>true</active>
        <errorMessage>Please select a General, Ministry, Partner or Worker Account.</errorMessage>
        <filterItems>
            <field>Account.RecordType.Name</field>
            <operation>equals</operation>
            <value>General Account, Ministry Account, Partner Account, CMS Account</value>
        </filterItems>
        <isOptional>false</isOptional>
    </lookupFilter>
    <referenceTo>Account</referenceTo>
    <relationshipLabel>Subscriptions</relationshipLabel>
    <relationshipName>Subscriptions</relationshipName>
    <required>false</required>
    <type>Lookup</type>
</CustomField>

The following code (which I was using prior to trying your library) works:

private static Id toRecordTypeId(Schema.DescribeSObjectResult sObjDescribe, string recordTypeDevName) { return sObjDescribe.getRecordTypeInfosByDeveloperName().get(recordTypeDevName).getRecordTypeId(); }

    Publication__c generalPublication = new Publication__c 
        ( Name                = SETUP_NAME_PUBLICATION
        , RecordTypeId        = toRecordTypeId(Schema.SObjectType.Publication__c, 'General_Publication')
        , Branch_Ownership__c = SETUP_BRANCH_OWNERSHIP
        , Status__c           = SETUP_PUBLICATION_STATUS
        , Email__c            = true
        );
    insert generalPublication;

    Account generalAccount = new Account
        ( Name                  = SETUP_NAME_GENERAL_ACCOUNT
        , RecordTypeId          = toRecordTypeId(Schema.SObjectType.Account, 'General_Account')
        , CMS_ID__c             = SETUP_ORGANISATION_CMS_ID
        , Branch_Ownership__c   = SETUP_BRANCH_OWNERSHIP
        , Status__c             = 'Active'
        , Organisation_Email__c = SETUP_ORGANISATION_EMAIL
        );
    insert generalAccount;

    Publication__c generalPublication = [ SELECT Id, Name FROM Publication__c WHERE Name = :SETUP_NAME_PUBLICATION LIMIT 1 ];
    Account generalAccount = [ SELECT Id, Name FROM Account WHERE Name = :SETUP_NAME_GENERAL_ACCOUNT LIMIT 1 ];

    Subscription__c sub = new Subscription__c
        ( Organisation__c       = generalAccount.Id
        , Publication__c        = generalPublication.Id
        , Email_Subscription__c = true
        , Status__c             = 'Subscribed'
        , Email_Preference__c   = 'Organisation Email'
        , Branch_Ownership__c   = SETUP_BRANCH_OWNERSHIP
        , RecordTypeId          = toRecordTypeId(Schema.SObjectType.Subscription__c, 'Organisation_Subscription')
        );

    insert sub;

The same issue is also happening with 'Publicationc' => fabPubGen (if I remove 'Organisationc' => fabAccGen) I tried removing both the organisation and publication key values from the map and then doing; sfab_FabricatedSObject fabSubOrg = new sfab_FabricatedSObject( Subscriptionc.class, subscriptionOrganisationMap ); fabSubOrg.setParent(Subscriptionc.Organisationc, fabAccGen); and also: fabSubOrg.setParent('Organisationc', fabAccGen); but these all yielded the same error.

Are you able to help?

Kind regards, Craig.

bobalicious commented 3 years ago

Apologies - answering on my phone, rather than at a PC, so I may be off - no ability to test.

Have you tried switching organisation c over to organisationr. I.E. is the relationship and not the id that you're setting.

Also, I would consider moving over to using 'set' to set up the fields, rather than the constructor. As far as I am aware, the original intention was for the constructor to be solely used when testing the library, rather than for end-users. Though I guess that's down to personal taste - now the API exists, it's unlikely to be removed.

On Wed, 2 Jun 2021, 03:08 cdonnelly-cms, @.***> wrote:

Hi, Thank you for providing this library. It sounds like a great way to improve unit test execution times.

I'm trying to use it for the first time and I'm getting the following runtime error:

sfab_FabricatedSObject.FieldIsNotParentRelationshipException: The field Subscriptionc.Organisationc is not a parent relationship Class.sfab_FabricatedSObject.checkFieldIsParentRelationship: line 534, column 1 Class.sfab_FabricatedSObject.setDirectParent: line 389, column 1 Class.sfab_FabricatedSObject.setParent: line 240, column 1 Class.sfab_FabricatedSObject.set: line 112, column 1 Class.sfab_FabricatedSObject: line 192, column 1 Class.sfab_FabricatedSObject.: line 58, column 1 Class.COMMS_subscription_TRIGGER_mc_TEST.after_update_organisation_subscription_happy: line 84, column 1

My code is:

Map<string,object> publicationGeneralMap = new Map<string,object>
    { 'Id'                  => 'Id-Pub-Gen-1'
    , 'Name'                => SETUP_NAME_PUBLICATION
    , 'RecordTypeId'        => toRecordTypeId(Schema.SObjectType.Publication__c, 'General_Publication')
    , 'Branch_Ownership__c' => SETUP_BRANCH_OWNERSHIP
    , 'Status__c'           => SETUP_PUBLICATION_STATUS
    , 'Email__c'            => true
    };
sfab_FabricatedSObject fabPubGen = new sfab_FabricatedSObject( Publication__c.class, publicationGeneralMap );

Map<string,object> accountGeneralMap = new Map<string,object>
    { 'Id'                    => 'Id-Acc-Gen-1'
    , 'Name'                  => SETUP_NAME_GENERAL_ACCOUNT
    , 'RecordTypeId'          => toRecordTypeId(Schema.SObjectType.Account, 'General_Account')
    , 'CMS_ID__c'             => SETUP_ORGANISATION_CMS_ID
    , 'Branch_Ownership__c'   => SETUP_BRANCH_OWNERSHIP
    , 'Status__c'             => 'Active'
    , 'Organisation_Email__c' => SETUP_ORGANISATION_EMAIL
    };
sfab_FabricatedSObject fabAccGen = new sfab_FabricatedSObject( Account.class, accountGeneralMap );

Map<string,object> subscriptionOrganisationMap = new Map<string,object>
    { 'Id'                    => 'Id-Sub-Org-1'
    , 'Organisation__c'       => fabAccGen
    , 'Publication__c'        => fabPubGen
    , 'Email_Subscription__c' => true
    , 'Status__c'             => 'Subscribed'
    , 'Email_Preference__c'   => 'Organisation Email'
    , 'Branch_Ownership__c'   => SETUP_BRANCH_OWNERSHIP
    , 'RecordTypeId'          => toRecordTypeId(Schema.SObjectType.Subscription__c, 'Organisation_Subscription')
    };
    sfab_FabricatedSObject fabSubOrg = new sfab_FabricatedSObject( Subscription__c.class, subscriptionOrganisationMap );

It is falling over on the last line. Subscription is a custom object. The field meta data for its Organisation field is:

<?xml version="1.0" encoding="UTF-8"?>

Organisation__c Restrict false If the subscription is for an Organisation, select the name of the organisation here. true Please select a General, Ministry, Partner or Worker Account. Account.RecordType.Name equals General Account, Ministry Account, Partner Account, CMS Account false Account Subscriptions Subscriptions false Lookup

The following code (which I was using prior to trying your library) works:

private static Id toRecordTypeId(Schema.DescribeSObjectResult sObjDescribe, string recordTypeDevName) { return sObjDescribe.getRecordTypeInfosByDeveloperName().get(recordTypeDevName).getRecordTypeId(); }

Publication__c generalPublication = new Publication__c
    ( Name                = SETUP_NAME_PUBLICATION
    , RecordTypeId        = toRecordTypeId(Schema.SObjectType.Publication__c, 'General_Publication')
    , Branch_Ownership__c = SETUP_BRANCH_OWNERSHIP
    , Status__c           = SETUP_PUBLICATION_STATUS
    , Email__c            = true
    );
insert generalPublication;

Account generalAccount = new Account
    ( Name                  = SETUP_NAME_GENERAL_ACCOUNT
    , RecordTypeId          = toRecordTypeId(Schema.SObjectType.Account, 'General_Account')
    , CMS_ID__c             = SETUP_ORGANISATION_CMS_ID
    , Branch_Ownership__c   = SETUP_BRANCH_OWNERSHIP
    , Status__c             = 'Active'
    , Organisation_Email__c = SETUP_ORGANISATION_EMAIL
    );
insert generalAccount;

Publication__c generalPublication = [ SELECT Id, Name FROM Publication__c WHERE Name = :SETUP_NAME_PUBLICATION LIMIT 1 ];
Account generalAccount = [ SELECT Id, Name FROM Account WHERE Name = :SETUP_NAME_GENERAL_ACCOUNT LIMIT 1 ];

Subscription__c sub = new Subscription__c
    ( Organisation__c       = generalAccount.Id
    , Publication__c        = generalPublication.Id
    , Email_Subscription__c = true
    , Status__c             = 'Subscribed'
    , Email_Preference__c   = 'Organisation Email'
    , Branch_Ownership__c   = SETUP_BRANCH_OWNERSHIP
    , RecordTypeId          = toRecordTypeId(Schema.SObjectType.Subscription__c, 'Organisation_Subscription')
    );

insert sub;

The same issue is also happening with 'Publicationc' => fabPubGen (if I remove 'Organisationc' => fabAccGen) I tried removing both the organisation and publication key values from the map and then doing; sfab_FabricatedSObject fabSubOrg = new sfab_FabricatedSObject( Subscriptionc.class, subscriptionOrganisationMap ); fabSubOrg.setParent(Subscriptionc.Organisationc, fabAccGen); and also: fabSubOrg.setParent('Organisationc', fabAccGen); but these all yielded the same error.

Are you able to help?

Kind regards, Craig.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/mattaddy/SObjectFabricator/issues/21, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABRYYBZQ4JVMBL6MQLPM4JTTQWHA5ANCNFSM455ZS4YQ .

cdonnelly-cms commented 3 years ago

Hi Rob,

Wow. Thank you for your quick response.

Using 'Organisation__r' seems to have fixed the problem. That's a great help.

There's a new error now relating to an Id field being null, but it may be something to do with my test code, so I'll do some debugging tomorrow.

Thank you for the tip on using the set method. I've switched over to: new sfab_FabricatedSObject(Subscription__c.class).set(subscriptionOrganisationMap);

Kind regards, Craig.

cdonnelly-cms commented 3 years ago

Hi again.

It looks like the null issue is occurring because the formula fields on the fabricated Subscription object are not populated.

Is there a way to get the formula fields to calculate or to manually populate them?

Kind regards, Craig.

cdonnelly-cms commented 3 years ago

After some experimentation I found that setting the value of the formula fields directly worked.

Trying to get the formulas to calculate using... sfab_FabricatedSObject fabSubOrg = new sfab_FabricatedSObject(Subscriptionc.class).set(subscriptionOrganisationMap); Subscriptionc sub = (Subscriptionc)fabSubOrg.toSObject(); List results = Formula.recalculateFormulas(new List<Subscriptionc> { sub }); system.debug(results[0]);

Resulted in the following:

USER_DEBUG|[96]|DEBUG|FormulaRecalcResult:[recalcFieldErrors=(FormulaRecalcFieldError:[fieldError=Field not supported: Publicationr. If this is lookup field, try setting the ID field instead., fieldName=Publicationr], FormulaRecalcFieldError:[fieldError=Field not supported: Organisationr. If this is lookup field, try setting the ID field instead., fieldName=Organisationr]), record=Subscriptionc:{Is_Currently_Mailable_Subscription__c=false, merge_Postcodec=null, Address_Barcodec=null, Postcodec=null, Subscriber_Deceasedc=false, mc_Is_Blank_Preferred_Email_Address__c=true, merge_Statec=null, mc_Is_Exclude_Contact_Individualc=false, Is_Mail_Address_Sufficient_for_Australia__c=false, Is_Subscription_for_Individualc=false, Email_Informal_Greetingc=null, Individual_Lastname__c=null, Subscription_Record_Type_Namec=Organisation Subscription, Statec=null, Publication_Name__c=null, Key4_Individual_Publication_Emailc=||1, Publication_Record_Type_Namec=null, merge_Street__c=null, Is_Mail_Country_Australiac=true, Publication_Statusc=null, Is_Mail_Address_Sufficient_for_Overseasc=false, Email_Formal_Greetingc=null, Email_Greetingc=null, temp_Is_Subscription_Email_Differentc=false, Email_Subscription__c=true, Email_Preferencec=Organisation Email, merge_Is_Contact_Subscribed_and_Mailablec=false, Id=a25000zzzz00001AAA, Statusc=Subscribed, Mail_Informal_Greetingc=null, Individual_Firstnamec=null, Emailc=null, Is_Subscribedc=true, merge_Suburbc=null, Suburbc=null, mc_Is_Sync_this_Record_Calcc=false, merge_Organisation_Namec=null, RecordTypeId=0126F000000o7jMQAQ, Streetc=null, Subscription_Channels_Availablec=No delivery options have been set for this item, merge_Mail_Formal_Greetingc=null, Countryc=null, merge_Contact_Lastnamec=null, Monthly_Mailing_Segmentc=null, Is_Mail_Street_Presentc=false, Organisation_Preferred_Namec=null, mc_Subscriber_Keyc=null, Branch_Ownership__c=NSW & ACT, Address_DPIDc=null, Addressc=null, merge_CMS_IDc=null, Mail_Formal_Greetingc=null, cc_Emailc=null, mc_Is_Exclude_Contact_Personc=false, mc_Is_Exclude_Organisationc=true, CMS_IDc=null, merge_Country__c=null, Is_Subscription_for_Organisationc=true, Individual_Full_Namec= , mc_Is_Excludedc=true}]

It looks like some of the formulas calculated but not the ones I needed.

bobalicious commented 3 years ago

I think this ticket is starting to get a little confused. If you need help understanding a concept, or think there might be a bug, can you try to reduce it to a much smaller illustration of the issue. Generally 5 or 6 lines should do the job.

If this is the first time you've picked up the library, I suggest you try it out with something simpler first and then build up to this.

Things I should point though:

In short though, I wish you luck, but I can't really support you with using the library unless you can reduce your test cases to something significantly smaller and ask a clear question / report a clearly described bug (I did this, I expected it to do this, but it did this).

It's not that I don't want to, it's that it isn't really possible.

On Thu, 3 Jun 2021, 06:01 cdonnelly-cms, @.***> wrote:

After some experimentation I found that setting the value of the formula fields directly worked.

Trying to get the formulas to calculate using... sfab_FabricatedSObject fabSubOrg = new sfab_FabricatedSObject(Subscriptionc.class).set(subscriptionOrganisationMap); Subscriptionc sub = (Subscriptionc)fabSubOrg.toSObject(); List results = Formula.recalculateFormulas(new List<Subscriptionc> { sub }); system.debug(results[0]);

Resulted in the following:

USER_DEBUG|[96]|DEBUG|FormulaRecalcResult:[recalcFieldErrors=(FormulaRecalcFieldError:[fieldError=Field not supported: Publicationr. If this is lookup field, try setting the ID field instead., fieldName=Publicationr], FormulaRecalcFieldError:[fieldError=Field not supported: Organisationr. If this is lookup field, try setting the ID field instead., fieldName=Organisationr]), record=Subscriptionc:{Is_Currently_Mailable_Subscription__c=false, merge_Postcodec=null, Address_Barcodec=null, Postcodec=null, Subscriber_Deceasedc=false, mc_Is_Blank_Preferred_Email_Address__c=true, merge_Statec=null, mc_Is_Exclude_Contact_Individualc=false, Is_Mail_Address_Sufficient_for_Australia__c=false, Is_Subscription_for_Individualc=false, Email_Informal_Greetingc=null, Individual_Lastname__c=null, Subscription_Record_Type_Namec=Organisation Subscription, Statec=null, Publication_Name__c=null, Key4_Individual_Publication_Emailc=||1, Publication_Record_Type_Namec=null, merge_Street__c=null, Is_Mail_Country_Australiac=true, Publication_Statusc=null, Is_Mail_Address_Sufficient_for_Overseasc=false, Email_Formal_Greetingc=null, Email_Greetingc=null, temp_Is_Subscription_Email_Differentc=false, Email_Subscription__c=true, Email_Preferencec=Organisation Email, merge_Is_Contact_Subscribed_and_Mailablec=false, Id=a25000zzzz00001AAA, Statusc=Subscribed, Mail_Informal_Greetingc=null, Individual_Firstnamec=null, Emailc=null, Is_Subscribedc=true, merge_Suburbc=null, Suburbc=null, mc_Is_Sync_this_Record_Calcc=false, merge_Organisation_Namec=null, RecordTypeId=0126F000000o7jMQAQ, Streetc=null, Subscription_Channels_Availablec=No delivery options have been set for this item, merge_Mail_Formal_Greetingc=null, Countryc=null, merge_Contact_Lastnamec=null, Monthly_Mailing_Segmentc=null, Is_Mail_Street_Presentc=false, Organisation_Preferred_Namec=null, mc_Subscriber_Keyc=null, Branch_Ownership__c=NSW & ACT, Address_DPIDc=null, Addressc=null, merge_CMS_IDc=null, Mail_Formal_Greetingc=null, cc_Emailc=null, mc_Is_Exclude_Contact_Personc=false, mc_Is_Exclude_Organisationc=true, CMS_IDc=null, merge_Country__c=null, Is_Subscription_for_Organisationc=true, Individual_Full_Namec= , mc_Is_Excludedc=true}]

It looks like some of the formulas calculated by not others.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/mattaddy/SObjectFabricator/issues/21#issuecomment-853563523, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABRYYB6IPFFM6IUS6HGPEZDTQ4EC3ANCNFSM455ZS4YQ .

cdonnelly-cms commented 3 years ago

Thanks. All good now.

Sorry for providing too much detail. I'll try and be more succinct next time.

I understand better now the use case for this library in setting read only formula fields and the difference between setting the c and r fields.

My test is now working. Thank you for all your help.

bobalicious commented 3 years ago

No worries, glad I was of help.

Good luck with your future use!

On Thu, 3 Jun 2021, 09:32 cdonnelly-cms, @.***> wrote:

Thanks. All good now.

Sorry for providing too much detail. I'll try and be more succinct next time.

I understand better now the use case for this library in setting read only formula fields and the difference between setting the c and r fields.

My test is now working. Thank you for all your help.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/mattaddy/SObjectFabricator/issues/21#issuecomment-853690512, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABRYYB7ELCG2DZAWWTUSGH3TQ443FANCNFSM455ZS4YQ .