I have a trigger on a custom object, Subscription__c, (child of Account) which is supposed to give the Account TelemarketingPrioritizedFor__c = 'Winback' if, and only if, the field NumberOfActiveSubscriptions__c is 0 -- that field is a roll-up field which counts the number of Subscription__c with StartDate__c and NO EndDate__c (ie. EndDate__c = null).

Now, in my trigger test I want to add a subscription and then subsequently update it with an EndDate__c -- which should cause the trigger to fire and mark the Account as Winback. However, this doesn't happen as the NumberOfActiveSubscriptions__c field remains '1' despite the only Subscription__c now having an EndDate__c. I know this is because Roll-up fields are not updated until after a trigger fires, but I don't know how to test the trigger properly then?

Here's the SubscriptionTrigger:

trigger SubscriptionTrigger on Subscription__c (after update) {
    * If the new sub has an EndDate and the old didn't, the sub just got cancelled.
    * In those cases, we should mark the Account as prioritized for Winback (causing the AccountTrigger to push them to Winback) and set a DCU of 3 months

    System.debug('SubscriptionTrigger activated!');

    Id winbackId;
    List<Campaign> winback = [SELECT Id FROM Campaign WHERE Type = 'Winback'];
    if (winback.size() > 0) winbackId = winback[0].Id;
    Id noshowId;
    List<Campaign> noshow = [SELECT Id FROM Campaign WHERE Type = 'No show'];
    if (noshow.size() > 0) noshowId = noshow[0].Id;

    * By using a List we can have a single insert DML command instead of a bunch of separate ones. Keeps the governor happy.
    List<Account> accsToUpdate = new List<Account>();

    * We do this additional query in order to check whether the person is a permanent no or has other active subscriptions.
    * This avoids situations where a person is marked as Dead__c, then later has their Subscription terminated, and is accidentally added to Winback
    Map<Id, Account> accMap = new Map<Id, Account>();
    for (Account a : [SELECT Id, TelemarketingPrioritizedFor__c, Dead__c, No_fundraising__c, NumberOfActiveSubscriptions__c FROM Account WHERE Id IN (SELECT Account__c FROM Subscription__c WHERE Id IN :Trigger.new)]) {
        accMap.put(a.Id, a);

    System.debug('SubscriptionTrigger will now check the Trigger subscriptions...');

    for (Subscription__c s : Trigger.new) {
        System.debug('SubscriptionTrigger checking a subscription...');
        Subscription__c oldSub = Trigger.oldMap.get(s.Id); 
        if (oldSub.EndDate__c == null && s.EndDate__c != null) {
            System.debug('SubscriptionTrigger found a Subscription that has been Ended! Its data is StartDate: ' + s.StartDate__c + ' and EndDate ' + s.EndDate__c + ' It will now check for permanent no on Account...');
            * Subscription just got cancelled. Provided that the person isn't a permanent no, prioritize him for winback
            Account acc = accMap.get(s.Account__c);
            System.debug('SubscriptionTrigger digged up the responsible Account with these info: Dead: ' + acc.Dead__c + ' and NF: ' + acc.No_fundraising__c + ' and NOAS: ' + acc.NumberOfActiveSubscriptions__c);
            if (acc.Dead__c == false && acc.No_fundraising__c == false && acc.NumberOfActiveSubscriptions__c == 0) {
                System.debug('SubscriptionTrigger found no permanent nos and is prioritizing for Winback...');
                acc.TelemarketingPrioritizedFor__c = 'Winback';

    if (accsToUpdate.size() > 0) update accsToUpdate;

and here is the test class for it:

public class SubscriptionTriggerTest {

    static void endingSubscriptionPrioritizesAccountForWinback() {

        // Setup
        Account acc = new Account(Name = 'Test Person');
        insert acc;
        Contact con = new Contact(AccountId = acc.Id, FirstName = 'Test', LastName = 'Person');
        insert con;
        Campaign winback = new Campaign(Name = 'Winback', Type = 'Winback');
        insert winback;

        // Add Subscription
        Subscription__c sub = new Subscription__c(Account__c = acc.Id, Amount__c = 100, StartDate__c = Date.today().addDays(-10));
        insert sub;

        // Adding EndDate should make the trigger prioritize the Account for Winback
        sub.EndDate__c = Date.today();
        update sub;

        Account actual = [SELECT Id, TelemarketingPrioritizedFor__c FROM Account WHERE Id = :acc.Id LIMIT 1];
        System.assertEquals('Winback', acc.TelemarketingPrioritizedFor__c);

The error I get when running the test is:

SubscriptionTriggerTest.endingSubscriptionPrioritizesAccountForWinback  System.AssertException: Assertion Failed: Expected: Winback, Actual: null

as mentioned, this is due to the NumberOfActiveSubscriptions__c field being == 1 and thus the Account is never updated.


You used acc instead of actual for the test, so it was guaranteed to fail. Here's what you meant to write:

System.assertEquals('Winback', actual.TelemarketingPrioritizedFor__c);

If you look at the Triggers and Order of Execution document, you'll see that since the Subscription was updated, the parent's rollup summary field (the account) will be correct at that point in your trigger.

In this case, it was a simple case of using the wrong variable. This effect happens because the database and the current transaction are considered two different areas of memory. Whatever you put in a sObject variable is only temporary and does not necessarily reflect the state of the database.

|improve this answer|||||
  • Damn, such a bad mistake... Anyway, even after fixing it the original problem persists: When the trigger fires, it still evaluates NumberOfActiveSubscriptions__c to being 1. Looking at your link, it seems like during the test trigger fires at Step 7, but the NumberOfActiveSubscriptions__c Roll-up isn't updated until Step 17? Isn't that why this test fails? And is there anything I can do to get around that problem? – user14040 Sep 25 '18 at 12:04

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.