9

For Apex testing, I'm trying to create an object that is the same as the one created with a SOQL statement (see following method). The method retrieves the Quote_To_Lease__c object from Salesforce and gets a Related List (Referral_Payment_Transaction__r). When I try to create this object and related list (see following test code) in Salesforce I get a syntax error "Field is Not Writeable: Referral_Payment_Transactions__r".

Is there a way to create a fake object which has a related list that looks like the real one created by a SOQL select?

Method

public static void getQtlWithRpTrans(){
    Quote_to_Lease__c qtl =
    [SELECT Id, Name 
        ,Opportunity__c, Opportunity__r.Name, Opportunity__r.Referred_By_Lookup__c, Opportunity__r.Property_Name__c
        , (select id, Name, FirstName__c, LastName__c, Status__c, Referral_Type__c 
            , Event_Name__c, Customer_Name__c, Email__c, Cost_Center__c, ExtId_Vendor_Account__c, Unit__c 
            , Date_Forwarded__c, Date_Fwd_Completed__c
            ,Contact__c, Lead__c, Opportunity__c, Referral_Payment_Account__c
        from Referral_Payment_Transactions__r) 
    FROM Quote_to_Lease__c
    where id = 'a0mQ0000003sM6wIAE'];
    Referral_Payment_Transaction__c rpTrans = qtl.Referral_Payment_Transactions__r[0];
    System.debug('Qtl=' + qtl.id
        + ', rpTrans=' + rpTrans.Id
        + ', rpTrans.DateForwarded=' + rpTrans.Date_Forwarded__c
    );
} 

Test code

// Create fake QTL object
    Quote_To_Lease__c qtl = new Quote_to_lease__c(
        id = RpTransQtl.fakeQtlId
        , name='L12345'
    );

    // Create fake RpTrans object
    Referral_Payment_Transaction__c rpTrans = 
        new Referral_Payment_Transaction__c(
            Id=RpTransQtl.fakeRpTransId
    );
    // Create a list to place in the related field
    List<Referral_Payment_Transaction__c> myRpTransList = 
        new List<Referral_Payment_Transaction__c>();
    myRpTransList.add(rpTrans);

    // Get syntax error on following line
    //   Field is not writeable: Referral_Payment_Transactions__r 
    qtl.Referral_Payment_Transactions__r = myRpTransList;
14
  • SOQL is a query language. It does not create objects. That said, you may perhaps be looking to use __c rather than __r to fetch the ID for your related object, which you'd use to set up the relationship. Commented Sep 11, 2014 at 20:13
  • Thanks for your comment. Create was the wrong word, retrieve the data from Salesforce using SOQL would have been better. Once the data is retrieved, the code will evaluate the fields in the object and related list. My challenge is to emulate that same object and related list in test code so I can check out my code's functionality without having to create an object with an insert. Commented Sep 11, 2014 at 20:20
  • __c did not work - invalid field. Commented Sep 11, 2014 at 20:23
  • Okay. Again, you cannot assign a value to the to-many relationship field. Your code should create one (or more) child objects, then assign the parent's ID to the lookup field on each child object. Commented Sep 11, 2014 at 20:24
  • The structure of the object in question did not formal well in my first post. Here is a simpler form in a Select SOQL Select statement which works: SELECT Id, Name , (select id, Name from Referral_Payment_Transactions__r) FROM Quote_to_Lease__c Commented Sep 11, 2014 at 20:27

3 Answers 3

10

You can simulate a query in JSON.

For your viewing pleasure, I present the following:

Quote_to_Lease__c[] qol = (Quote_to_Lease__c[])JSON.deserialize(
    '[{"Id":"Real-looking-id", "Name": "001", "Referral_Payment_Transactions__r":['+
    '{"totalSize":1,"done":true, "records":[{"Id":"Another-real-Id","Name":"002"}]}'+
    ']}]',
    List<Quote_to_Lease__c>.class
);

Also included is this real code I used in my developer org to arrive at this answer:

Account a = (Account)JSON.deserialize('{"Name":"Test", "Contacts": {"totalSize":1, "done":true, "records":[{"FirstName":"Brian","LastName":"Fear"}]}}', Account.class);

You can also build your own multi-level map, JSON-ify it, then deserialize it back into the desired target, if you wanted to.

6
  • I should have remembered this ...as I've used the equivalent when deserializing REST SOQL GET from a remote SFDC into a local SFDC.
    – cropredy
    Commented Sep 11, 2014 at 22:11
  • That's a smokin' hot answer! What a cunning plan. I'll be using the technique in the future. Commented Sep 11, 2014 at 23:09
  • We are making heavy use of a class called SObjectFactory which does leverage the JSON approach. It allows us to set parent and child objects and formula/rollup-summary fields. This does result in lean and fast tests (sometimes by factor 10), because we do not need to insert everything into the database. It also allows to write much better unit tests, if there is no trigger ruining your test data on insert. Without it, good testing with APEX is not manageable. Commented Sep 12, 2014 at 7:19
  • and in the smoking hot vein, you can put the JSON into a staticResource as it is much easier to build/edit JSON outside of APEX strings and then do a SOQL Select on the static resource to fetch the JSON for deserializing
    – cropredy
    Commented Sep 12, 2014 at 16:25
  • and, this smoking hot answer also solves this problem - salesforce.stackexchange.com/a/49317/2602
    – cropredy
    Commented Sep 12, 2014 at 19:18
2

SFDC Apex can use the relationship field differently when constructing objects depending on use case

// Account has related list of Contacts
Account a = new Account();
a.contacts = new List<Contact> {new Contact()}; // fails - can't add related list

// Contact has a parent Account
Contact c = new Contact();
c.account = new Account();  // works, can add parent obj

In your scenario, you are trying to build the relatedlist without actually doing DML in your testmethod. As you have seen, this does not work without using the json.deserialize approach shown by @sfdcfox.

1
  • There is a way, where there's a will.
    – sfdcfox
    Commented Sep 11, 2014 at 21:14
0

You have to add the relationships in your test data. Here's a simple example of an object (program) with a child object (state program):

Program_Cycle__c pc = new Program_Cycle__c();
pc.Name = 'Test';
pc.Fiscal_Year__c = '2012';
pc.Type__c = 'Summer';
insert pc;
...
//State Program
// just gets a mostly pre-filled object
StateProgram__c objTestStateProgram1 = TestMethodClass.getStateProgram(); 
objTestStateProgram1.Program_Cycle__c = pc.Id;  //AAA

List<StateProgram__c> lstStateProgram = new List<StateProgram__c>();
lstStateProgram.add(objTestStateProgram1);
insert lstStateProgram;  //BBB

Each relationship must be set and added to the database. Adding the ID of the program to the state program (line AAA) isn't enough. It must be added to the database as well (line BBB). If the relationships are more complex (eg. junction objects), those objects would need to be filled out with the correct IDs and saved as well.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .