2

So I have 3 orgs that are basically all on the same codebase. On the production org (the one that gets changed the least) some of the tests are suddenly failing - all with the same error message:

System.DmlException: Insert failed. First exception on row 0; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: S2XTransaction: []

First of all - I have no clue what S2XTransaction is but it must be some kind of internal Salesforce object. Second - these tests are kinda like Schrödinger's cat because when I run them and check their results they are failing. If all the tests are run as part of the deployment of a changeset - all of them are passing! Over 90% of the classes haven't been touched in the last couple of years - so why is this error suddenly occurring? And why only on 1 out of 3 orgs? And even then why does it only happen when I run the tests directly?

So far my reasoning is this: Something must have been changed as there were no problems about 6 weeks ago. The code wasn't changed much and there was almost nothing that was deployed from the two other orgs to production. And even then all the changes were small, not affecting the now failing classes in any way. Right now my guess is that it could have something to do with this New user permission to view user roles and hierarchy change that got auto-activated at the beginning of the year.

The code that is actually failing in every test looks like this:

// This line below is failing
System.runAs(getTestAdminUser()) {
    // Run some code
}

// Just getting the test user
public static User getTestAdminUser() {
    User user = new User();

    // Set properties
    user.Alias = 'admin';

    return user;
}

I'm actually not quite sure what I'm even looking for. This is so confusing and I really hope somebody can shed some light on this.

7
  • I'm assuming your comment of setting stuff is then performing a DML on User object right? Why would you be setting fields on the User when all you really need to do is retrieve an active User?
    – TSmith
    Feb 13 '18 at 12:19
  • No DML here - just setting the properties. Why? I don't know. I just inherited this code and it's always been that way.
    – Semmel
    Feb 13 '18 at 12:45
  • What is also interesting: The user is actually never inserted into the database - no DML is being run. Is this being done implicitly through runAs - starting a new transaction?
    – Semmel
    Feb 13 '18 at 13:01
  • 2
    I think when you do runAs with a not yet existing user, it implicitly inserts the user to use for that code block. Presumably the behaviour of this until now has been that the user insert does not count against either the inner or outer block for the purpose of mixed DML errors. Maybe in Spring '18 they screwed this up? One possibility to try is to insert the user yourself before the runAs block. If this still causes problems, wrap the insert statement in a runAs block but just run as the current user.
    – Charles T
    Feb 13 '18 at 13:06
  • I think S2XTransaction is part of SalesForce-to-SalesForce functionality, do you have that enabled? Feb 13 '18 at 14:04
4

Calling system.runAs is a DML Statement. It inserts the record if it's not already in the database, which looks like your current setup. You should query for your admin user instead.

static User getAdminUser()
{
    return [SELECT Id FROM User WHERE Profile.Name = 'System Administrator'];
}
4
  • Learned something new (including the automatic insert if the runAs User doesn't already exist). BTW, I would do return [Select Id from User where ID = :UserInfo.getUserId()][0] to guarantee user is active. Also avoids locale issues where org is not english language
    – cropredy
    Feb 14 '18 at 0:05
  • Definitely should filter on IsActive, that's my b.
    – Adrian Larson
    Feb 14 '18 at 0:10
  • System.runAs itself is not a DML statement. But as I wrote above, it is implicitly doing an INSERT if not done before - which is a DML statement. Either way - selecting the user is not an option as the tests require special test users. Don't ask me why - I just inherited this stuff.
    – Semmel
    Feb 14 '18 at 18:08
  • Then you need to run as an existing user to insert that User record beforehand, then run as the inserted User to do your test.
    – Adrian Larson
    Feb 14 '18 at 18:10
3

I just wanted to let you know that after months of testing and escalating the Salesforce support was finally able to find out what the issue was.

The first part of the puzzle is that only the tests that insert Contact records inside the System.runAs are failing. Lightning Sync is set up in production which - quite obvious - keeps Contacts in sync. Behind the scenes, this implicitly inserts S2XTransaction records which in turn are conflicting with the creation of our User records.

See: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dml_non_mix_sobjects.htm

The suggestion was to move the creation of the users out of the test methods and into the @TestSetup. This seems to fix the issue.

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.