0

I still learning here but I am having a bit of trouble understanding this concept.

I have a method kind of like this:

public static List<Contact> newContacts() {
        List<Contact> syncList = new List<Contact>();
        Map<Id, Set<String>> lookerByIds = new Map<Id, Set<String>>(); 
        lookerByIds = class.MethodCall(classTwo.callList);
        //this method call parses a list into a map
        List<Contact> sfdcList = [SELECT Id, AccountId, Email, LastName, 
        FROM Contact WHERE AccountId IN : lookerByIds.keySet()];

       //other code
}

I want to test this and all the logic in it but how do I set the value of the Map within the unit test? I have something like this:

@isTest static void testUpdateContacts() {

    Test.startTest();

    List<Contact> testCon = new List<Contact>();
    testCon.add(new Contact(AccountId = '0014F00000UtgwqQAB', LastName ='Accounts Payable', status__c = 'Billing Contact', Email = 'Email2@e.com'));
    testCon.add(new Contact(AccountId = '0014F00000UtgwqQAB', LastName ='George', status__c = 'Billing Contact', Email = 'Email1@e.com'));
    insert testCon;
    //sets the Contact data for the test
    //calls method being tested
    //test code etc
    Test.stopTest();

How do I set the value of my Map so that this unit test will use it?

1

The primary lever you have to control behavior in a unit test is data.

Unit tests execute in an isolated data context. You seed that context with records whose values are designed to force your code down specific execution paths.

Here, two things to look at.

One, you can't do this:

testCon.add(new Contact(AccountId = '0014F00000UtgwqQAB', LastName ='Accounts Payable', status__c = 'Billing Contact', Email = 'Email2@e.com'));
testCon.add(new Contact(AccountId = '0014F00000UtgwqQAB', LastName ='George', status__c = 'Billing Contact', Email = 'Email1@e.com'));

Those Account Ids don't exist in the isolated test context. You'll need to insert Accounts within the test context, and use their Ids to associate with these Contacts.

Two, you need to look at this code:

    Map<Id, Set<String>> lookerByIds = new Map<Id, Set<String>>(); 
    lookerByIds = class.MethodCall(classTwo.callList);

and consider

  1. What is the value of classTwo.callList in the test context? Remember, that class can only see the test data you specifically generate and seed in this context.
  2. What does class.MethodCall do, and how can you manipulate its logic to produce the desired result in lookerByIds, so that your further downstream logic executes the code path you are interested in testing?

If these questions are very difficult to answer, you may consider refactoring your code with testability in mind. Testable code uses small, clean units of logic that are easily composed into larger units, avoids hiding state, and when necessary allows unit tests to inject state via the @TestVisible annotation.

For more about writing good unit tests, please see our collection of canonical resources.

|improve this answer|||||
1

With what you have right now, there isn't a way for you to directly put data into lookerByIds. You're forced to accept whatever class.MethodCall() returns.

If you can modify classTwo.callList in your unit test, then you have some degree of control, but then your "unit"1 test (for the newContacts() method) has a dependency on another class (alternatively, it is strongly coupled to another class). Your "unit" test ends up testing more than one "unit", and dependencies inside of classes/methods tend to make maintenance harder (if you update a dependency, it has a chance of breaking the things that depend on it).

There are a few different ways you could re-design your classes/methods to make testing easier, like using the Stub API to mock the class.MethodCall() call so that it returns data that you can specify (doesn't work for static methods, and a few other things), or Dependency Injection (like changing your newContacts() method so that it accepts a Map<Id, Set<String>> parameter).

The gist of things is that you need to specifically design them for testability.

1: There isn't really a precise definition of what a "unit" is in "unit testing". In general, I take it to mean "try to test as small of a piece of code that makes sense and actually produces a useful result". Thinking about a single method as a "unit" is a good place to start.

|improve this answer|||||
  • ApexMocks being an alternative to native StubAPI though for new learners, a bit of a slog – cropredy Jan 15 at 1:46
0

There are definitely other ways to answer your question, but one way would be to add a line in your method to return a different value if a test is running.

ex:

public static List<Contact> newContacts() {
  .
  .
  List<Contact> sfdcList = [SELECT Id, AccountId, Email, LastName, 
                           FROM Contact WHERE AccountId IN : lookerByIds.keySet()];

  if(test.isRunningTest()){
      sfdcList = [SELECT Id, LastName from Contact WHERE ....];
   }
}

This way, when you invoke this method from a test class, test.isRunningTest() will be true and the other query will be executed for test purposes.

|improve this answer|||||

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.