6

I moved a method that sets addError() from a trigger into a Helper class. As I started writing test code for it it seems like the addError() is not actually preventing DML operations for the records that get modified in the helper class.

The information from the SF SObject class page states: Marks a trigger record with a custom error message and prevents any DML operation from occurring.

Does this mean that addError() will not work outside of a trigger class?

EDIT

Adding some sample code.

public class MyHelperClass() {
    public static void someMethod(Opportunity opp) {
        opp.My_Field__c.addError('Some error');
        insert opp;
    }
}

And sample test for it without any asserts:

@IsTest
private class MyHelperClass_Test() {
    @IsTest
    static void someTest() {
        Opportunity opp = new Opportunity();
        opp.Name = 'Some Name';
        opp.StageName = 'Prospect';
        opp.CloseDate = Date.today().addDays(30);

        MyHelperClass.someMethod(opp);
    }
}

The above inserts the opportunity and does not trigger an exception. Am I doing something wrong?

  • addError works in Visualforce controllers, Visualforce extensions, and anywhere within a Trigger context. It might be you simply made a mistake somewhere; it won't block DML operations on records not involved in Trigger.new or Trigger.old (for delete events only). We probably need to see the actual code to help you. – sfdcfox May 14 at 16:09
  • @sfdcfox I added sample code. Let me know if this makes more sense? – Arthlete May 14 at 16:15
6

It only works on trigger context records, but it can be applied to those records outside of a trigger. You cannot call this method on a record which is not yet in a trigger context, then have the error carry through to the trigger context.

This code won't prevent DML:

Account record = new Account();
record.addError('You cannot insert this record');
insert record;

However, this code will:

trigger Account on Account (before insert)
{
    PreventDml.validate(trigger.new);
}
public with sharing class PreventDml
{
    public void validate(List<SObject> records)
    {
        for (SObject record : records)
        {
            record.addError('You cannot insert this record');
        }
    }
}

If you are trying to test this code, the only realistic, effective way to make sure it does what you want is to run the trigger. For example, in this scenario I might have a test like:

@IsTest
class AccountTriggerTests
{
    @IsTest static void testPreventDml()
    {
        DmlException expectedException;
        Test.startTest();
            try
            {
                insert new Account();
            }
            catch (DmlException dmx)
            {
                expectedException = dmx;
            }
        Test.stopTest();

        system.assertNotEquals(null, expectedException,
            'You should not be able to insert any Account');
        system.assertEquals(0, [SELECT count() FROM Account],
            'The database should be unchanged');
    }
}
  • That's very similar to my use case. How would you cover the line of code for record.addError('You cannot insert this record'); if you were to write test code for the PreventDml class alone? Is there a way to assert that the addError was set alone? – Arthlete May 14 at 16:33
  • 1
    @Arthlete Unlike other service methods, validation can only be effectively tested by running the trigger. There are hacks like checking ApexPages.hasMessages(), but they may not work in future releases, nor in all contexts even in the current release. – Adrian Larson May 14 at 16:44
  • Thank you, I will remember this! – Arthlete May 14 at 16:46
4

addError won't block future DML operations, only those already in progress. You can use addError in any class, but it won't have any effect on future operations. As an example of what would work:

trigger myFieldBlock on Opportunity {
  myFieldBlock.validate(Trigger.new);
}

public class myFieldBlock {
  public static void validate(Opportunity[] records) {
    records[0].My_Field__c.addError('Some error');
  }
}

You can also use addError to display errors on a Visualforce page:

public class VFController {
  public Account record { get; set; }
  public VFController() {
    record = new Account();
  }
  public void showError() {
    record.Name.addError('Some error');
  }
}

<apex:page controller="VFController">
  <apex:form>
    <apex:inputField value="{!record.Name}" />
    <apex:commandButton value="Show Error" action="{!showError}" />
  </apex:form>
</apex:page>
  • i did not get this. We add addError in trigger to prevent DML operations from occurring. Your first snippet should prevent any DML operation on the record as its been called from Trigger Context. However in VF snippet even if there is any DML after we add error, it will execute. – Anshul Agrawal May 15 at 13:15

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.