If you regularly write client-side customisations for Dynamics 365 forms, you should strongly consider writing tests for them.

To do so, you will require a mock implementation of Dynamics' Xrm namespace object: the object you use to manipulate the form with functions such as getAttribute and setRequired. This is because though your code knows about the Xrm object when it's running on a Dynamics form in the browser, your tests won't know about it when running locally on your machine.

Mock the Xrm object

I've begun creating a mock implementation of Xrm here on my GitHub page. It's usage is straightforward, and you can follow my examples if you're using TypeScript to write your client-side customisations.

First, clone the repository using npm install xrm-mock.

Then, create a file for your entity's form. Here's an example for the contact entity:

export namespace Company.Contact {
   export namespace MainForm {
       let contact: Contact;

       export function onLoad(xrm?: Xrm.XrmStatic): void {
           contact = new Contact(xrm || Xrm);
           contact.changeFirstName("Joe");
       }
   }

   class Contact {
       constructor(xrm?: Xrm.XrmStatic) {
           Xrm = xrm || Xrm;
       }

       changeFirstName(newName: string): void {
           Xrm.Page.getAttribute("firstname").setValue(newName);
       }
   }
}

Note:To use this script on a Dynamics form, just add Company.Contact.MainForm.onLoad as one of the form's onLoad() event handlers.

Then, create a second file to test your contact script. The below example is using jasmine.

import * as XrmMock from "xrm-mock";
import { Company } from "../src/contact";

describe("contact", () => {
    beforeEach(() => {
        let attributes: Xrm.Collection.ItemCollection<Xrm.Page.Attribute> = new XrmMock.ItemCollectionMock([
            new XrmMock.AttributeMock("firstname", "Phil", false, "required")
        ]);

        this.Xrm = new XrmMock.XrmStaticMock(
            new XrmMock.PageMock(
                new XrmMock.DataMock(
                    new XrmMock.EntityMock(
                        attributes
                    )
                )
            ));
    });
    it("works", () => {
        Company.Contact.MainForm.onLoad(this.Xrm);
        let firstName: string = Xrm.Page.getAttribute("firstname").getValue();

        expect(firstName).toBe("Joe");
    });
});

Now run your tests

You'll now need to configure a test runner. If you've followed my example, you can install jasmine by running the following command in your terminal: npm install jasmine --save-dev. This assumes you have node installed on your machine.

Combined with jasmine, I use wallaby.js to visualise my tests as I'm writing my code. Here it is in action: