1

We are integrating with a managed package that our company bought. We've inserted a custom VF page with an apex controller into their UI and I'm now trying to test the controller. In the constructor it makes some SOQL queries with object ids passed to the page. The ids are of objects in the managed package.

As I understand, the standard approach is for the test to create all the data the controller would need. However, creating these objects appears to trigger all sorts of triggers and flows in the managed package, which expect other objects to exist, validates data, etc.

All I want is to make a simple mock of an object and fill in one or two fields, so that my controller can query them and move on. I'm not at all interested in the triggers and flows that are part of the managed package, I want to unit test just my controller. What's the proper way to do this?

3 Answers 3

1

With managed package integrations, sometimes you are forced to use seealldata=true in the test class. It is sometimes impossible to setup test data (due to limits) through the packages normal flow.

Seealldata=true is like a last resort. You then put a dependency on data being setup in the running org, properly and complete to the point that your customization can function as expected. But sometimes there is no better choice.

Alternative is to somehow create mock data in the package object directly using DML. Or create the mock data but don't insert with DML, but set the controller.objectVar to your temp record.

You may need changes in the controller to facilitate a workaround. That's the downside to seealldata=false in this situation (having to modify the real code just to get a test to operate).

It's a situation where "it depends" on many factors like risk, cost, quality etc. Best practices should be followed when best fit, this may fit or may not.

5
  • Isn't what I'm doing an extremely common use-case though? To be able to unit test your code without having to worry about various triggers and dependencies other people may have written. Even if the triggers weren't managed and I could see them, I wouldn't want to go through other people's code just to figure out how to create some mock data. I feel like being able to disable events is a must-have for unit testing.
    – Egor
    Commented Jul 25, 2016 at 22:22
  • @Egor Not an exact match for what you are looking for but see e.g. success.salesforce.com/ideaSearch?keywords=disable%20triggers. The problem though with this ideas forum is that it is hard to get compelling vote numbers e.g. >> 5,000 for development oriented features.
    – Keith C
    Commented Jul 25, 2016 at 22:33
  • And even at 5K+ votes it does not mean it will even be looked at. There are ideas with >20K votes that are many many years old. I think the vote count is just a tease
    – Eric
    Commented Jul 26, 2016 at 1:09
  • @Egor - I would reach out to the developer of the managed package and see if they can write a class that will set up minimal required config data for their package to run. Many packages that use intricate configuration object include this to help subscribers continue to test. One downside is the Moc callout when made on insert or edit of a standard object though - With the new rules of Callouts failing instead of being skipped it is tough to get a test class in place unless they allow you to stop their triggers from calling out
    – Eric
    Commented Jul 26, 2016 at 1:11
  • I ended up wrapping my DML calls in an if(!isUnderTest), which is a public bool that I set during testing. We decided as a team that it was the least bad approach. Took an extra long shower that night, trying to scrub away the dirty feeling.
    – Egor
    Commented Jul 26, 2016 at 17:26
2

There's only one "reasonable" path: the ISV should build a global class that includes methods that you can use to correctly build all the required data. I don't think I've ever seen this done in a package, but it's not outside the realm of possibility. Try asking. Everything else is just a bad idea. However, sometimes, we're left with bad ideas because all the good ones aren't available.

While you asked about not creating all the records, your best choice, outside of ISV intervention, would be to create all the records. It's a pretty lousy choice, but thankfully, you generally only have to do this once. You might even decide to put all the data in a static resource and use Test.loadData.

If you really can't afford the time, then use SeeAllData=true. This lets you use live data, so use your best discretion here. Dependencies on live data can break your deployments at a critical moment, so make sure those records are protected or tucked away somewhere so some user doesn't accidentally modify/delete them. For example, assign them to a system administrator to reduce the odds that they'll get changed/deleted.

Also, depending on your code, you might be able to fake some of the records. This gets pretty interesting if you're not careful, but I've managed to deserialize complicated objects with relationships, then inject them at critical points in the code. I'll see if I can find an example in my old source code, but you should know that JSON.deserialize can definitely work around situations where you don't need to DML against or query the records in question (i.e. the code is designed simply to read or write field values).

2
  • you hit the nail on the head (as always). Try doing this for FinancialForce Accounting (record, groups, queues, setup and non setup objects, oh my). What a process that was......
    – Eric
    Commented Jul 26, 2016 at 1:12
  • This is for a CPQ engine, so it has some pretty complicated relationships with products, quotes, prices, proposals, options, etc. We talked about it as a team and the decision was to modify the controller to not do DML when it's being tested. Ugly as sin, but it was quick and easy, and the controller is quite small. We didn't want to invest too much time into something we may (hopefully) not need again.
    – Egor
    Commented Jul 26, 2016 at 17:34
2

If you are (very) lucky the managed package provider will have implemented a globally visible custom setting to turn off those constraints that you can set programmatically in your test. Through bitter experience, we now do this for our managed packages. (This switch is also valuable when doing some bulk data loads.)

But more commonly, you will have to fix each error in turn by modifying the data you create. Remember that if you add your own local validation you will have to code for that too. So your test setup ends up pathologically coupled to logic that you don't want it to be coupled to... A sad fact of life on the platform. If you are writing several classes yourself and therefore tests for those class, creating your own test data builder class so the coupling is located in one place makes sense.

You must log in to answer this question.

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