• Jim Cripe
  • NEWBIE
  • 0 Points
  • Member since 2008

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 4
    Questions
  • 7
    Replies

I had a query against an "Auto Number" column in an object using an Integer variable in a test method.

The test started failing in Spring '09.  I figured out why and thought I would share what I found (I haven't found a good example for querying AutoNumbers with Integer variables so I thought I would share how to do that too.)

In Spring '09, the Apex test record generator apparently started creating large integers instead of the next values in the normal sequence for the Auto Number field, and that revealed a problem in the code.  My test method was working when the values SaleForce generated for AutoNumbers when in the 300s range, but when it started feeding 10000 and above, the test started failing.)

Here is some example code:

Integer myAutoNumber_ID = 12340; //The test case was generating a large auto number in a record. (This example isn't best practice, but an example only. You should always create a test record and query it back to get the AutoNumber value generated to query with.)

//Test 1: Assertion will fail
MyObject__c[] mo = [select ID from MyObject__c where myAutoNumber_ID = : myAutoNumber_ID.format() ];
System.assertequals(1, mo .size(), 'MyObject size via format failed! No record found!');

Test 2: Ok
MyObject__c[] mo = [select ID from MyObject__c where myAutoNumber_ID = : '' + myAutoNumber_ID ];
System.assertequals(1, mo .size(), 'MyObject size via concatentation failed! No record found!');

 

In the "Test 1" example above, the assertion fails because the Integer "format" function inserts commas in the string it generates, and the Auto Number field doesn't have commas in it.  Once Auto Number starts being generated with values large enough to have commas, no record will ever be found because number strings like '1234' don't match those with commas like '1,234'.

"Test 2" above goes through.  The values to search for have to use the same type as the field queried, ("MyObject__c[] mo = [select ID from MyObject__c where myAutoNumber_ID = : myAutoNumber_ID ]" won't even compile.)

Concatenating an empty String to the Integer forces Apex to convert the Integer to a String value, but doesn't insert any commas in the resultant String so the record is found and the assertion passes.

Message Edited by Jim Cripe on 01-28-2009 08:27 AM
I may have come across another SF bug or at least a documentation hole.  I turned in a case, which I’m told was attached to an existing case.

 

If you have run this code, the assertion will fail in Winter '09:

 

global class TestCallByReferenceClass{

 

public static testMethod void testFunction(){

    Contact con = new Contact();

    System.debug('testFunction- con before: ' + con);

    Function1(con);

    System.debug('testFunction- con after: ' + con);

    System.assertEquals('TestLast',con.LastName); //Assertion will fail!

}

 

public static void Function1(Contact con){

    System.debug('Function1- con before: ' + con);

    con = new Contact();

    con.LastName='TestLast';

    System.debug('Function1- con after: ' + con);

}

}


To explain what happens in Apex, I need to give a couple of definitions:  "Call by value" means that a function actually gets a copy of a variable to use inside the function.  "Call by reference" means that the variable passed into a function is the variable itself.  Call by reference should make changes to a parameter variable inside the function be in the parameter variable after the function call ends because the variable outside and inside the called function are one and the same.


Apex is supposed to pass all objects by value, but there is a bit of a twist.


It turns out that the Contact "con" parameter in the code above is created as a reference variable to the real object and that reference variable is passed using call by value into the function.

 

The affect of this is that the "con" reference parameter that the function starts executing with is reassigned to hold a reference to the new object and forgets about the original object it was referencing.  When the function returns, the outer "con" object is still there untouched, but the "con" object with its LastName change went away with the copy of the reference variable that had existence only inside the function.


If you remark out the "con = new Contact();" line in Function1, the assertion does not fail because there is no reference variable reassignment.  The outer Contact "con" hooks to the same "con" inside the function so the changed LastName value is available when the function finishes.

 

This is the way Java behaves and I understand SF is programmed in Java.

 

SalesForce needs to document clearly if Apex behaves properly in this case, or fix Apex so that the function parameters to objects pass newly created objects out to be available when the function exits.




Message Edited by Jim Cripe on 01-15-2009 09:19 PM
The Eclipse Force.com IDE Schema Browser doesn't display "OldValue" and "NewValue" column values for History tables.

To see the problem, open the Schema Browser, (click on "salesforce.schema" at the bottom of the project in package explorer,) and click on any "__History" table. (A table for which "track field history" is turned on.) When you click on the checkbox to create a query with all fields and run the query, (you might want to add a "limit 100" to the query first,) you will see all the columns, and every column has values except "OldValue" and "NewValue".

If you query the history table through another means, such as SQL Server/DBAmp, the values show in the columns.

My Force.com IDE version is up to date at 13.0.0.200806130415.


Message Edited by Jim Cripe on 10-06-2008 10:23 AM
The Apex parser has a bug that won't allow a backslash to end string constants.

Try "System.debug('\\');" to see the problem. 

The compiler thinks the string is unterminated and gives compile error "line breaks not allowed in string literals".

I have a workaround:

String DirectoryPath = '\\\\test\\file\\path';

String BackSlash = '\\this'; //Try to remove 'this' from the string and try to run to see the bug
BackSlash = BackSlash.replace('this', ''); //Hack to be able to build backslash terminated character string

DirectoryPath = DirectoryPath + ( DirectoryPath.EndsWith(BackSlash) ? '' : BackSlash );

System.debug(DirectoryPath);

Has anyone else noticed some unexpected behavior with the latest version of the Data Loader, version 21.0?  Here is a list of just some of the issues that I've come across that do not occur when using previous versions of the Data Loader:

 

  1. When attempting to update a small subset of fields contained in the .csv file but not all (ie. mapping only a few fields), the Data Loader throws an the Error:  Failed to find mapping for '<one of the unmapped fields in the .csv file>'.  However, if all the fields in the .csv file are mapped, it appears to work correctly.  Previous versions had no problem with mapping only a subset of fields contained in the .csv file, my guess is this is a bug.
  2. After successfully completing an Export, the Data Loader stops responding to any action button click for Insert, Update, Upsert or Delete.  However, it will let you choose Export or Export All.  I cannot tell if this one is a new "feature" or a bug.
Anyone else experiencing any similar issues?  
Thanks in advance!

 

I may have come across another SF bug or at least a documentation hole.  I turned in a case, which I’m told was attached to an existing case.

 

If you have run this code, the assertion will fail in Winter '09:

 

global class TestCallByReferenceClass{

 

public static testMethod void testFunction(){

    Contact con = new Contact();

    System.debug('testFunction- con before: ' + con);

    Function1(con);

    System.debug('testFunction- con after: ' + con);

    System.assertEquals('TestLast',con.LastName); //Assertion will fail!

}

 

public static void Function1(Contact con){

    System.debug('Function1- con before: ' + con);

    con = new Contact();

    con.LastName='TestLast';

    System.debug('Function1- con after: ' + con);

}

}


To explain what happens in Apex, I need to give a couple of definitions:  "Call by value" means that a function actually gets a copy of a variable to use inside the function.  "Call by reference" means that the variable passed into a function is the variable itself.  Call by reference should make changes to a parameter variable inside the function be in the parameter variable after the function call ends because the variable outside and inside the called function are one and the same.


Apex is supposed to pass all objects by value, but there is a bit of a twist.


It turns out that the Contact "con" parameter in the code above is created as a reference variable to the real object and that reference variable is passed using call by value into the function.

 

The affect of this is that the "con" reference parameter that the function starts executing with is reassigned to hold a reference to the new object and forgets about the original object it was referencing.  When the function returns, the outer "con" object is still there untouched, but the "con" object with its LastName change went away with the copy of the reference variable that had existence only inside the function.


If you remark out the "con = new Contact();" line in Function1, the assertion does not fail because there is no reference variable reassignment.  The outer Contact "con" hooks to the same "con" inside the function so the changed LastName value is available when the function finishes.

 

This is the way Java behaves and I understand SF is programmed in Java.

 

SalesForce needs to document clearly if Apex behaves properly in this case, or fix Apex so that the function parameters to objects pass newly created objects out to be available when the function exits.




Message Edited by Jim Cripe on 01-15-2009 09:19 PM
The Apex parser has a bug that won't allow a backslash to end string constants.

Try "System.debug('\\');" to see the problem. 

The compiler thinks the string is unterminated and gives compile error "line breaks not allowed in string literals".

I have a workaround:

String DirectoryPath = '\\\\test\\file\\path';

String BackSlash = '\\this'; //Try to remove 'this' from the string and try to run to see the bug
BackSlash = BackSlash.replace('this', ''); //Hack to be able to build backslash terminated character string

DirectoryPath = DirectoryPath + ( DirectoryPath.EndsWith(BackSlash) ? '' : BackSlash );

System.debug(DirectoryPath);