1

I recently put an Apex Trigger into production.

Here is the code:

trigger FlagMLSTasks on Task (after insert) {
//if mls alert subject
Task t = [SELECT Id, Subject, CreatedDate, WhoId FROM Task WHERE Id IN: Trigger.newMap.keySet()].get(0);
if (t.Subject.contains('New MLS Listing Alert For')) {
  MtgPlanner_CRM__Transaction_Property__c loan = [SELECT MtgPlanner_CRM__Closing_Date__c FROM MtgPlanner_CRM__Transaction_Property__c WHERE MtgPlanner_CRM__Borrower_Name__c =: t.WhoId ORDER BY MtgPlanner_CRM__Closing_Date__c DESC limit 1];
    if (loan.MtgPlanner_CRM__Closing_Date__c == null){
        return;
    }
    Date taskCreateDate = date.newinstance(t.CreatedDate.year(), t.CreatedDate.month(), t.CreatedDate.day());
    Date loanCloseDate = date.newinstance(loan.MtgPlanner_CRM__Closing_Date__c.year(), loan.MtgPlanner_CRM__Closing_Date__c.month(), loan.MtgPlanner_CRM__Closing_Date__c.day());
    if (Math.abs(taskCreateDate.daysBetween(loanCloseDate)) < 5){
        t.Status = 'Completed';
        t.False_MLS_Task__c = True;
        update t;
    }
}

}

I was getting the following error:

FlagMLSTasks: execution of AfterInsert caused by: System.QueryException: List has no rows for assignment to SObject

I understand the error, so I fix it in the sandbox env.:

trigger FlagMLSTasks on Task (after insert) {
//if mls alert subject
Task t = [SELECT Id, Subject, CreatedDate, WhoId FROM Task WHERE Id IN: Trigger.newMap.keySet()].get(0);
if (t.Subject.contains('New MLS Listing Alert For')) {
    List<MtgPlanner_CRM__Transaction_Property__c> loans = [SELECT MtgPlanner_CRM__Closing_Date__c FROM MtgPlanner_CRM__Transaction_Property__c WHERE MtgPlanner_CRM__Borrower_Name__c =: t.WhoId ORDER BY MtgPlanner_CRM__Closing_Date__c DESC];
    if (loans.size() == 0 || loans.get(0).MtgPlanner_CRM__Closing_Date__c == null){
        return;
    }
    MtgPlanner_CRM__Transaction_Property__c mostRecentLoan = loans.get(0);
    Date taskCreateDate = date.newinstance(t.CreatedDate.year(), t.CreatedDate.month(), t.CreatedDate.day());
    Date loanCloseDate = date.newinstance(mostRecentLoan.MtgPlanner_CRM__Closing_Date__c.year(), mostRecentLoan.MtgPlanner_CRM__Closing_Date__c.month(), mostRecentLoan.MtgPlanner_CRM__Closing_Date__c.day());
    if (Math.abs(taskCreateDate.daysBetween(loanCloseDate)) < 5){
        t.Status = 'Completed';
        t.False_MLS_Task__c = True;
        update t;
    }
}

}

However, I'm having trouble trying to migrate the updated trigger into production ( a problem I did not get before when first migrating the trigger).

Here is the error I get when attempting to deploy:

    System.UnexpectedException: No more than one executeBatch can be called from within a test method. Please make sure the iterable returned from your start method matches the batch size, resulting in one executeBatch invocation.
Stack Trace: External entry point

It seems to me that both the trigger in live is triggering the same time my new trigger is being deployed (when you deploy you have to run tests to ensure proper coverage before it is actually released into live).

My problem with this error is that I never got this before. Anytime I want to make an update to a trigger, i just make the necessary changes and then on deployment it should replace the old trigger of the same name. Why is this happening with this particular trigger? How do I go about fixing it? I'm weary of deleting the old trigger (as I think this maybe what is causing me not to be able to deploy the updated version), is there any quick way of addressing this?

Here is the test that goes along with both the old and new trigger (I used the same test).

    @isTest(SeeAllData=true)
public class TestFlagMLSTasks {

    @isTest static void testConfirmedFalseMLS(){
        Contact c = [SELECT Id FROM Contact WHERE Id = '0031U00000R8yKmQAJ'];
        Task t = new Task(Subject = 'New MLS Listing Alert For', WhoId = c.Id, Status = 'Not Started');
        insert t;
        System.assert(!t.False_MLS_Task__c);
    }

}
2
  • using seeAlldata=true is the road to ruin; testmethods should mock all data required so your tests are isolated, regardless of what environment they run in; the specific error you are getting suggests that a testmethod indirectly causes two batchables to be scheduled (perhaps because of seelalldata=true)
    – cropredy
    Jan 6, 2020 at 19:10
  • Sadly, though, seeAllData=true is my only option to deploy; there is a managed package that is erroring when I insert mock data. We have a installed a framework on top of Salesforce.
    – Peabrain
    Jan 6, 2020 at 19:12

2 Answers 2

1

Regardless of the cause of that specific failure (which does not appear to be within the code shown here), this trigger and its unit test most certainly need to be rewritten anyway.

Since none of this code uses Batch Apex, presumably some other code or automation in your org is doing so. It is likely, but not certain, that the presence of seeAllData=true is the cause, since all downstream code in this unit test context is exposed to all of the real data in the org.

Test Class

Your test uses seeAllData=true and hard-codes record Ids. Both are very bad practices and establish a dependency between your test class and the specific data in the org in which you originally wrote it.

With hard-coded Ids, your test won't pass outside its original context - i.e., a different org with different data, or the same org after data has been altered. You will need to rewrite your test to ensure that it generates all of the data that it needs within its isolated test context, and remove the seeAllData=true annotation.

Additionally, your test fails to re-query Task data before making an assertion:

    System.assert(!t.False_MLS_Task__c);

meaning that its result is not meaningful. You must query changed objects to get their updated field values in order to evaluate the behavior of your code.

Trigger

Your trigger is not bulkified.

If more than one Task is modified in a single invocation, the trigger will only take action on the first one. All others will be ignored.

9
  • You are on the nose with the other code/automation doing the batch apex.
    – Peabrain
    Jan 6, 2020 at 19:16
  • 2
    Yes, I do. Removing seeAllData=true will very likely fix the Batch Apex error.
    – David Reed
    Jan 6, 2020 at 19:18
  • 2
    Working with managed code that does extensive validation on standard objects is a huge pain. If you can figure out how to satisfy it, I'd recommend building a test data factory class so you can use it in all your testing.
    – David Reed
    Jan 6, 2020 at 19:19
  • 1
    I'm just surprised that the original deployment worked; no errors. it is only on the re-deployment do I get this error.
    – Peabrain
    Jan 6, 2020 at 19:23
  • 2
    @peabrain - N.B. some managed code (e.g. CPQ) have features to disable triggers for their objects, check doc
    – cropredy
    Jan 6, 2020 at 19:29
1

A few things:

1. System.QueryException: List has no rows for assignment to SObject

This is generally caused by assigning an SObject to a SOQL dynamic query, like you are doing in:

Task t = [SELECT Id, Subject, CreatedDate, WhoId FROM Task WHERE Id IN: Trigger.newMap.keySet()].get(0);

I suggest assigning the SOQL to List<Task> and doing a .isEmpty() check before trying that .get(0)

2. You're doing an update on a Task in an after insert context. This could cause recursion which could run the trigger many times over and hit unexpected errors. Do you need to do this code in an after trigger context?

3. System.UnexpectedException: No more than one executeBatch can be called from within a test method.

Within your unit tests, using Test.startTest() and Test.stopTest() to ensure any asynchronous code is executed by the end of Test.stopTest()

3
  • 1
    +1, but for (3), using Test.startTest() and friends won't eliminate that error - it comes from overexposure of real data and/or overbroad SOQL in the batch class.
    – David Reed
    Jan 6, 2020 at 19:15
  • for (1) isnt it ensured that there should be one task in the query because the trigger is after an insert of a new task?
    – Peabrain
    Jan 6, 2020 at 19:19
  • for (2) I am never inserting a new task, just updating one, and even if we did a trigger that is based on an update, it shouldnt be making a task with the subject " new mls ..." so it may run at most once and partially a second time around but it should stop.
    – Peabrain
    Jan 6, 2020 at 19:21

You must log in to answer this question.

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