How do you write a class for a Dynamics CRM form in TypeScript that is testable?
By testable, I mean a non-static class that can be instantiated and passed a mock Xrm object.
My current approach is something like this, but it has limitations (which I explain):
export class Contact {
Xrm: Xrm.XrmStatic;
constructor(xrm?: Xrm.XrmStatic) {
this.Xrm = xrm;
}
onLoad(): void {
if (this.Xrm) { Xrm = this.Xrm; }
// ...
}
}
Firstly, the Contact
class is exported so that can be referenced by a CRM form. CRM can't instantiate an object before calling it's functions, so to call these methods I use Contact.prototype.onLoad in the CRM form designer.
My test looks like this:
beforeEach(function () {
this.XrmMock = new XrmStaticMock();
this.contact = new Contact(this.XrmMock);
this.contact.onLoad();
}
it('does stuff', function () {
// ...
}
The test is able to instantiate Contact
and pass XrmMock
to the constructor. Subsequently when onLoad()
is called, it evaluates if (this.Xrm)
to true, and uses XrmMock
. Conversely, when Contact.prototype.onLoad()
is called from within CRM, (this.Xrm)
is false because Contact
has never been instantiated. Therefore any reference to Xrm
within onLoad()
uses the default Xrm
namespace (which is what we want). We want this because when a CRM form is opened, the real Xrm
dependency is loaded by the browser.
The limitation here is having to write a conditional check in every method which wants to use the Xrm
dependency. How can this be overcome?
Here's a link to documentation on the Xrm.Page object model. My classes and Xrm mock are written using @types/xrm.
I appreciate I'm asking multiple questions here, if you think you can guide me to writing a more specific question, please let me know :)
Why would you call the Contact.prototype.onLoad
method within CRM and not call a method that instantiates your Contact class in the same way as you do in your test but using CRM's Xrm
object? E.g.
function ContactOnLoad() {
var contact = new Contact(Xrm);
contact.onLoad();
}
Then in your form's OnLoad handler you call ContactOnLoad
. If you just call Contact.prototype.onLoad
, this
might refer to your window object and not a new instance of Contact
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With