4

I am facing Mixed DML error in the following case:

static testmethod void method1()
{
    User u1 = DataFactory.createUser('testUser','[email protected]','permissionSetAPIname');  

    Obj1 rec1 = DataFactory.createrec1('Record1');
    insert rec1;

    Obj2 rec2 = DataFactory.createrec2('Rec2',rec1,system.today()-10, system.today()+10 );
    insert rec2; 

    Obj3 rec3 = DataFactory.createrec3(rec2,'Tester',u1,'US');
    insert Role;

    test.starttest();   

    //testing code

    test.stoptest();

}

However the following is working fine

static testmethod void method1()
{
     User u1 = new User(Alias = 'testRole',
                  Email             = '[email protected]',
                  EmailEncodingKey  = 'UTF-8',
                  LastName          = 'user',
                  FirstName         = 'test',
                  LanguageLocaleKey = 'en_US',
                  LocaleSidKey      = 'en_US',
                  ProfileId         = [select Id from Profile where Name = 'Basic User' limit 1].Id,
                  TimeZoneSidKey    = 'America/Chicago',
                  Username          = 'stakeholder@test' + Math.round(Math.random() * 10000) + '.com',
                  UserRoleId        = null,
                  Country           = 'Great Britain');
    insert u1;   

    Obj1 rec1 = DataFactory.createrec1('Record1');
    insert rec1;

    Obj2 rec2 = DataFactory.createrec2('Rec2',rec1,system.today()-10, system.today()+10 );
    insert rec2; 

    Obj3 rec3 = DataFactory.createrec3(rec2,'Tester',u1,'US');
    insert Role;

    test.starttest();   

    //testing code

    test.stoptest();

}

Please explain the difference. As my understanding is that transaction remains the same in both the cases.

0

2 Answers 2

8

You must specify UserRoleId as null. Reference:

You can insert a user in a transaction with other sObjects in Apex code saved using Salesforce API version 15.0 and later if UserRoleId is specified as null.

The obvious conclusion is that your DataFactory.createUser method is setting the UserRoleId to some other value.


If you must specify this field for some reason, you need to isolate your User creation in a separate transaction. One way to cross the boundary into a separate transaction is to enter or leave a system.runAs block. The only logic in this separate transaction should be to insert any User records.

//  set up User records
system.runAs(new User(Id=UserInfo.getUserId()))
{
    insert userRecords;
}
// other setup

Note that since only admins can create User records, it may be best to query for one so the test will work regardless of who runs it.

User SYS_ADMIN = [
    SELECT Id FROM User WHERE IsActive = true
    AND Profile.Name = 'System Administrator'
];
system.runAs(SYS_ADMIN) { /*logic*/ }
2
  • Hey Adrian . Your inputs helped. I however have failed to understand the difference between the two cases. How does putting system.runAs(currentUser) differ from not putting anything. In both cases the test class runs in system mode, that is current user mode.
    – starhunter
    Commented Apr 29, 2016 at 7:58
  • @star check out the link on how transactions work.
    – Adrian Larson
    Commented Apr 29, 2016 at 8:46
2

You could wrap your test setup in a System.runAs() block to avoid this.

e.g.

User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()];

System.runAs(thisUser) {
    // perform necessary setup/testing here
}

This method is referenced in the Apex Developer Guide linked here

Specifically noted: These techniques enable you, for example, to create a user with a role and other sObjects in the same test.

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