Don't worry about coding defensively against your client's configuration; they're allowed to ignore certain types of test failures. This is noted in the documentation:
If a subscriber creates a validation rule, required field, or trigger on an object referenced by your package, your test might fail if it performs DML on this object. If this object is created only for testing purposes and never at runtime, and the creation fails due to these conflicts, you might be safe to ignore the error and continue the test. Otherwise, contact the customer and determine the impact.
The most important thing is that your code does not fail unit tests during upload, including code coverage. This is the only time you need to fix your code.
I can answer some of these I think. At least we are now creating new unlocked package versions and promoting them. Our unlocked package version does depend on two managed packages.
1 - Does SF use some internal/hidden scratch org to actually deploy package code there - to make sure that apex code and sobjects are valid?
Yes, as on the reference docs "Creates a package version in the Dev Hub org." so this command takes what you have in your local source folder and then uses via the DevHub org spins up a transient scratch org and checks the metadata deploys there.
The build can fail here if for example your apex code references an unmanaged field that is present in the scratch org but hasn't been pulled locally. Also I believe it matters what you have in your project-scratch-def for the package, if for example your app depends on Notes or Chatter being enabled in the org then you must include that in project scratch def or the package will not build.
2 What if our package depends from AppExchange package - how does SF make sure that everything is fine with references to those external classes and sobjects?
You need to declare these dependencies in sfdx-project.json, below is a snippet of ours where we do this, referencing the two managed packages that the unlocked package depends on.
"packageDirectories": [
{
"path": "arcus-crm-core",
"default": true,
"package": "arcus-crm",
"versionName": "ver 0.6",
"versionNumber": "0.6.0.NEXT",
"definitionFile": "config/project-scratch-def.json",
"dependencies": [
{
"package": "04t0a000001rJyj"
},
{
"package": "04t0a000001xvUU"
}
]
},
When you do this and create a new version then it will install the dependent packages into the transient scratch org. (I can tell as the time taken for the build increases substantially).
3 Does it execute all tests as well as in case of 1GP package upload? Do we have here same restrictions, e.g. all tests should succeed, >=75% of coverage etc.
Update for Winter 21 Code coverage is not enforced when creating a new version (always created as beta) but from the Winter release you wont be able to promote a beta version to release (requred for production installs) unless it meets 75% code coverage requirements. Source
sfdx force:package:version:promote --package "<alias of package version to promote>"
4 - Assuming we converted our whole 1GP package to 2GP one (without any splitting, I know this is not quite good, but still) - could we expect that package version creation would be faster/same/longer?
Im guessing but I suspect the same in terms of time for commands to run. I think overall its quicker though as the flow for developing, pulling, committing packaging etc is a lot smoother and easier to do from Vscode.
I cant vouch for how up to date it is but I found this FAQ really useful for gettingto grips with unlocked packages.
https://sfdc-db-gmail.github.io/unlocked-packages/faq-unlocked-pkgs#toc
Hope that helps!
Best Answer
Unless you're intentionally trying to keep the base object decoupled from the package, simply use:
If you are trying to keep the base object out of your package, use:
To be clear,
Schema.getGlobalDescribe()
can be very expensive and should only be used as a last resort (e.g. you need to list every object anyways). I've seen this method take up to 3 seconds on the first call, plus an additional second for each call thereafter.Also, this is one example of why your tests are probably taking two hours. Tests run in serial mode only when testing code coverage this way, so if your tests are expensive, they will take a while to run. If you have a lot of smaller tests, they will likely take longer to run than fewer larger tests. That's just the nature of how transactions work in Salesforce.
Anecdotally, I once worked on a project that had a class with hundreds of "constants", and because of how they were working, that class, which was called from every other class, added a full 1 second of CPU time to every transaction, multiplied by the 500 or so unit tests we had to run. I refactored it so it lazy-loaded the data, and immediately reduced our deployment time by about 7 minutes. It also made every transaction in the database 1 second faster, which the users noticed. The point is, do some profiling and figure out how to optimize away your time killers.
Properly written code won't have this problem. There are very few exceptions when a managed package and unmanaged code will behave differently, and most of the time, you can work around them by either checking your namespace or by using alternative methods.
One important aspect is that you need to understand that everywhere you need namespaces, there are ways to get the code to behave correctly. Avoiding
Schema.getGlobalDescribe()
is one such example. Others include preferring to use tokens instead of strings (e.g.CustomObj__c c = new CustomObj__c()
instead ofCustomObj__c c = (CustomObj__c)Schema.getGlobalDescribe().get('CustomObj__c').newsObject()
). Your code, if properly written, should be namespace agnostic.Also, one final note: when you are developing, you can create a scratch org with the same namespace as your 2GP. See Link a Namespace to a Dev Hub Org for more information. This allows you to always work with a namespace, which provides you a more accurate way to determine why code is failing.