10

In java , we perform mocking to test the database classes. Is there any way to test the classes in apex language & mocking also works in apex test cases.

Now how will i perform these test cases to cover the code with or without mocking ?

8

You do not need to mock database queries. You can insert test data, perform queries, updates, deletions within a test method. All database operations are rolled back at the end of the test. See An Introduction to Apex Code Test Methods.

| improve this answer | |
  • 2
    There is a big overhead with inserting test data, both with setting it up and with test execution time. A better solution is to avoid use of SOQL all over the code and implement a database layer which can itself be mocked. – NickJ Dec 1 '16 at 13:15
8

As alluded to in previous answers to this question, Salesforce provides

  1. an ORM built right into the Apex language
  2. a testing framework that rolls back DML operations after any tests run

Points 1. and 2. together with the lack of a declarative construct for working with in memory collections (such as that found in .Net LINQ ) makes it very difficult to make a case for testing without the use of the underlying Salesforce database.

That said , I have provided some code below that includes:

  1. a test class ,

  2. an interface and

  3. contrasting C#/Java and Apex implementations

Key

  • T01 = Test class
  • I01 = Record Provider interface
  • CM01 = Mock implementation of the interface in .Net
  • AM01 = Mock implementation of the interface in Saleforce Apex
  • P01 = Actual provider implementation of the interface in Saleforce Apex (uses SOQL)

This may be what you are asking for in your question. Again , keep in mind that .Net (Java?) provides a declarative syntax with LINQ which goes a long way to support the pattern I believe you are after. The key point I am attempting to make is that there is very little to be gained in hand rolling imperative code to implement a Mock in Apex.

// T01
class Tests {

    static List<Custom__c>  getMockRecords() {
        return List<Custom__c> records = new List<Custom__c> {
            new Custom__c( Name = "Frank") ,
            new Custom__c( Name = "Adam") ,
            new Custom__c( Name = "Evan") };
    }

    // T02
    static void testMockProvider() {
        // Arrange
        IRecordProvider provider = new MockRecordProvider( getMockRecords()  );

        // Act  / Asserts , etc
        ... = provider.getRecordsByNameDescending() ;
        ... = provider.getRecordsByNameAscending() ;    
    }

    // T03
    static void testProvider() {
        // Arrange
        IRecordProvider provider = new RecordProvider(  );

        // Act  / Asserts , etc
        ... = provider.getRecordsByNameDescending() ;
        ... = provider.getRecordsByNameAscending() ;    
    }

    // T04
    // tests on domain layer operations 
    static void testOnSomethingThatOperatesOnSortedCollection() {
        // Arrange
        IRecordProvider provider = new MockRecordProvider( getMockRecords()  );
        DomainSomething something = new DomainSomething(  IRecordProvider provider);

        // Act  / Asserts , etc
        ... = something.OperateOnAscendingCollectionFromProvider();
    }


}






// I01
public interface IRecordProvider{

    // I02
   List<Custom__c>   getRecordsByNameDescending() ;

    // I03
   List<Custom__c>   getRecordsByNameAscending() ;
}




//C#:
// CM01
public MockRecordProvider implements IRecordProvider{

    private List<Custom__c>  records = new List<Custom__c> ();

    public MockRecordProvider (List<Custom__c>  records) {
        this.records = records;
    }

    /// CM02
    public List<Custom__c>   getRecordsByNameDescending() {
        // LINQ :: declarative code here 
        return  FROM r IN this.records
                ORDERBY r.Name DESCENDING
                SELECT r;
    }

    // CM03
    public List<Custom__c>   getRecordsByNameAscending() {
        // LINQ :: declarative code here 
        return  FROM r IN this.records
                ORDERBY r.Name ASCENDING
                SELECT r;
    }
}


// Apex::
// AM01
public MockRecordProvider implements IRecordProvider{

    private List<Custom__c>  records = new List<Custom__c> ();

    public MockRecordProvider (List<Custom__c>  records) {
        this.records = records;
    }

    //AM02
    public List<Custom__c>   getRecordsByNameDescending() {
        // lots of imperative sort code here  - Not a good idea!
        // return sorted list
    }

    //AM03
    public List<Custom__c>   getRecordsByNameAscending() {
        // lots of imperative sort code here - Not a good idea!
        // return sorted list
    }
}



// PA01
public RecordProvider implements IRecordProvider{


    public RecordProvider () {
    }

    // PA02
    public List<Custom__c>   getRecordsByNameDescending() {
        // SOQL :: declarative code here 
        return [SELECT Id, Name FROM Custom__c ORDER BY Name DESC];
    }

    // PA03
    public List<Custom__c>   getRecordsByNameAscending() {
        // SOQL :: declarative code here 
        return [SELECT Id, Name FROM Custom__c ORDER BY Name ASC];
    }
}

As a final comment I will switch sides :

I can think of very rare situations where you may want to work without the salesforce database because the underlying SObjects have thousands of validation rules and triggers firing out of the wazoo.

The scenario : You are the time-pressured consultant initiating a new project at a new client. Instead of getting on with the logic around the collections( T04 in the example code), you find that arranging your test data becomes a nightmare of bizarre business logic rules firing millions error messages at you during DML operations during the arranging phase of test setups. In this specific situation you can perhaps consider deferring the nightmare and just get on with the business at hand by making use of Mocks per the example code above.

However, following this path will result in you encountering a second problem: How to link a complex object graph (eg Parent - Detail) without making using use of extraneous External Id fields on the underlying database schema. One solution is to roll your own SObject Id Generator class as Salesforce doesn't provide an out-of-the-box solution for the equivalent of :

(new System.Generator( Type objectType)).getNextId() ;

I have provided some rudimentary code below that may get you started on such a Utility. Although ugly, I find that it gets the job done during those rare occasions when I really really need to initially bypass the database and still build good tests. I "stole" most of the code from someone on StackExchange. Unfortunately I cannot attribute appropriately as I have forgotten the gent's name.

Usage :

IdUtility customIdProvider = IdUtility(Schema.SObjectType.Custom__c.Name);
Id customId = customIdProvider.getNext();

Id Generator Utility

public class IdUtility {
    private integer m_Sequence = 0;
    private string  m_Prefix   = null;

    public  IdUtility(string sObjectName )     {
        m_Prefix   =  getPrefix( sObjectName);
    }

    private string  getPrefix(string sObjectName)   {
       string result = null;

       try  {    
            Type t = Type.forName(sObjectName );
            Sobject  so =  (Sobject) t.newInstance();
            Schema.SObjectType sot =  so.getSObjectType();
            Schema.DescribeSObjectResult r = sot.getDescribe();
            result = r.getKeyPrefix();
        }  catch (Exception  e )  {    
            throw new IdUtilityException('Cannot resolve key prefix for SObject=['+sObjectName+']');
        }

        return result;
    }

    public Id getNext()   {
        m_Sequence++;
        string sequenceString = string.ValueOf(m_Sequence);

        String id = m_Prefix + '0000A0000000' ;
               id = id.left(id.length() - sequenceString.length() );
               id = id + sequenceString ;


        string suffix = '';
        integer flags;

        for (integer i = 0; i < 3; i++) {
            flags = 0;
            for (integer j = 0; j < 5; j++)   {
               string c = id.substring(i * 5 + j,i * 5 + j + 1);
               if (c.toUpperCase().equals(c) && c >= 'A' && c <= 'Z')                 {
                    flags = flags + (1 << j);
               }
            }

            if (flags <= 25) {
               suffix = suffix + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.substring(flags,flags+1);
            }  else   {
               suffix = suffix + '012345'.substring(flags-25,flags-24);
            }
        }

        return (Id) (id + suffix);
    }

    public class IdUtilityException extends Exception {}
}
| improve this answer | |
  • The mock ID generator is pretty clever - another use case for this is if you are testing a REST service that copies an object graph from a source org into the target REST service's org. Your test method needs to generate source JSON data representing the object graph in the simulated source system org. Without your solution, the workaround is to create SObjects in the testmethod and then deleting them - you get valid IDs that I could use in the test JSON input data. – cropredy Jan 16 '14 at 2:35
1

You will have to write a test class. In that test class you can cover all the test cases in separate methods.

you can take help from here : http://wiki.developerforce.com/page/An_Introduction_to_Apex_Code_Test_Methods

Thanks

| improve this answer | |
  • just read my question again and do comment after understanding the problem. I am asking mock test for database queries written in methods. – upgoingstar Jan 15 '14 at 7:05
  • So to clarify: you want someting that approximates an interface IDataProvider { List<x__c> GetRecs(); } where getRecs() is implemented as a SOQL query against the database in DataPovider and faked with a mocked up collection of x__c in MockDataProvider ? Correct? – max Jan 15 '14 at 9:16
0

Maybe possible you can use the built-in method:

System.Test.setFixedSearchResults(List<Id>)

The list of record IDs specified by opt_set_search_results replaces the results that would normally be returned by the SOSL queries if they were not subject to any WHERE or LIMIT clauses. If these clauses exist in the SOSL queries, they are applied to the list of fixed search results.

developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_test

It's intended for use on SOSL queries. But if you don't mind the mental mapping, this could be a neat way to mock your database result set without having to touch the production code itself. For example:

return [
    SELECT Id, Name, Industry, Phone, Fax
    FROM Account
    WHERE Name LIKE '%red%'
];

This can be translated into SOSL:

return [
    FIND '* OR *' RETURNING Account (
        Id, Name, Industry, Phone, Fax
        WHERE Name LIKE '%red%'
    )
][0];

Then your test class can use the built-in platform mocking mechanism:

@IsTest public class FixedSearchResultsTest {

    /**
     * Production code uses SOSL query...
     */
    static List<Account> findRedOnes() {
        return [
            FIND '* OR *' RETURNING Account (
                Id, Name, Industry, Phone, Fax
                WHERE Name LIKE '%red%'
            )
        ][0];
    }

    /**
     * Test code directly injects fake result set...
     */
    static testmethod void testFindRedOnes() {
        //arrange mock account
        Account account = new Account(Name = 'red');
        insert account;

        //act
        Test.setFixedSearchResults(new List<Id>{account.Id});
        List<Account> accounts = findRedOnes();

        //assert
        Integer expectedSize = 1;
        Integer actualSize = accounts.size();
        System.assertEquals(expectedSize, actualSize, 'Wrong size.');

        Id expectedId = account.Id;
        Id actualId = accounts[0].Id;
        System.assertEquals(expectedId, actualId, 'Wrong Id.');
    }

}
| improve this answer | |
-1

first of all download virtual box and sandbox setting. Then Use virtual box to create virtual environment. Tun your class in virtual box and test method on your machine. It worked for me. Hope that solvs your issue. If you have any queries feel free to ask

| improve this answer | |
  • This sounds like it could be a legit answer, so you should take some time to flesh this out and make it more usable to someone who doesn't have your knowledge. Or maybe reference a blog post or video that will help the asker understand what helped you solve the problem. – pchittum Apr 9 '15 at 10:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.