Find your content:

Search form

You are here

Why is my reference field returning null my test code?

 
Share

I am trying to test some code that is working in the sandbox. The code, shown below, uses a reference to obtain the BusinessHoursId field of the Milestone's Case object (m.Case__r.BusinessHoursId):

public static Integer getNumMinutesTaken(Milestone__c m) {
    Long diffInMillis = BusinessHours.diff(m.Case__r.BusinessHoursId, m.Start__c, m.CompletionDatetime__c);
    return (Integer)diffInMillis / (1000 * 60);
}

This works fine in the sandbox, as I have said, but for some reason I cannot use the same type of reference in my test code to obtain the BusinesshoursId:

// Test Code Snippet
Case c = getValidCase();
c.BusinessHoursId = defaultBH.Id;
insert c;
System.debug('c.Id'+c.Id); // valid id
System.debug('c.BusinessHoursId'+c.BusinessHoursId); // valid id

Datetime start = datetime.newInstance(2012, 10, 5, 16, 0, 0);
Datetime completed = datetime.newInstance(2012, 10, 5, 17, 0, 0);

Milestone__c ms = new Milestone__c(Case__c = c.Id, Start__c = start, CompletionDatetime__c = completed);

System.debug(ms.Case__r.BusinessHoursId); // null
Integer timeTaken = MilestoneUtils.getNumMinutesTaken(ms);

System.assertEquals(60, timeTaken);

As far as I can see, this is the same reference as in my method, but it is returning null. Is there a difference to the allowable ways of referencing things in test code as compared to live code?


Attribution to: Joe

Possible Suggestion/Solution #1

Can you please insert that milestone first of all and first check.

If still error the following conclusion :

Relationship reference dont work in Test classes i feel unless you explicitly query using __r relationship field .

Need to query once where id is inserted Id .


Attribution to: Mohith Shrivastava

Possible Suggestion/Solution #2

I would expect this to always return null, regardless of context. The fact that it is working some of the time is a surprise to me.

Generally, unless you have explicitly queried for fields on a related record, a reference using the relationship name will return null or throw a 'field not queried' exception. In your case, if you want to refer to the field ms.Case__r.BusinessHoursId, you would need to insert the ms record, then query for that same record:

insert ms;
ms = [select Id, Case__r.BusinessHoursId from Milestone__c where Id = :ms.Id];
System.debug(ms.Case__r.BusinessHoursId); //should give you the right value

Added as explanation:

The lookup field itself (ms.Case__c) can be thought of as simply a text/Id field with a primitive String value in it. The relationship name, ms.Case__r actually refers to an entire object that is sort of stuck onto the object you queried (ms) as an object attribute. You can use additional relationships to retrieve more related objects, such as ms.Case__r.Account.Name, in which you can think of ms.Case__r.Account as being an entire object based on the field ms.Case__r.AccountId.

In a trigger, all fields on the triggering object are available, but no fields on related objects are available, so one of the most common Trigger patterns involves collecting related record Ids and querying for them separately from the triggering records. In most other contexts, only the fields you query for will be in the object record you're working with. In the case of your getNumMinutesTaken method, you are assuming that the milestone record you're passed was queried on fields Case__r.BusinessHoursId, Start__c and CompletionDatetime__c. You're not querying for or referring to Case__c, since you don't need to know the actual Id of the Case, just some fields on the Case object.


Attribution to: Jeremy Nottingham

Possible Suggestion/Solution #3

Generally you do have to explicitly query for the related fields, though in test methods it is actually possible to fabricate relationships in memory without committing records to the database:

Account a = new Account(Name='MyAcc');
Opportunity o = new Opportunity(Name='MyOpp',Account=a);
OpportunityLineItem oli = new OpportunityLineItem(Opportunity=o);
system.debug(oli.Opportunity.Account.Name);

So here I have assigned the Account sobject to Opportunity.Account instead of assigning a.id to Opportunity.AccountId.

The same works for custom objects:

ObjectOne__c o1 = new ObjectOne__c(Name='MyObj1');
ObjectTwo__c o2 = new ObjectTwo__c(ObjectOne__r=o1);
ObjectThree__c o3 = new ObjectThree__c(ObjectTwo__r=o2);
system.debug(o3.ObjectTwo__r.ObjectOne__r.Name);

However, you cannot assign a list of sobjects to a detail relationship - for example adding a list of Opportunity Line Items to an Opportunity.


Attribution to: Stephen Willcock
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/1885

My Block Status

My Block Content