Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Unit test Web API calls with xrm-mock and SinonJS

Setting the scene

We write JavaScript to perform business logic client-side on Dynamics forms.

  • Sometimes we want to dynamically alter the layout of a form (collapse tabs, hide sections etc.)
  • Sometimes we want to update field values
  • And sometimes we want to retrieve data from Dynamics using the Web API and then act on it

No matter why we're writing client-side scripts, we always want to write tests for them.

I've previously written in this blog post and others how basic Xrm functions can be tested against using xrm-mock. However, what about more advanced Xrm functions such as Xrm.WebApi?

Introducing the Web API with v9

Xrm.WebApi was introduced with Dynamics 365 version 9. In Microsoft's words, it: "Provides properties and methods to use Web API to create and manage records and execute Web API actions and functions in Customer Engagement".

My interpretation would be that it enables developers to interact with the Web API using shorthand code. For example, prior to version 9, one would write the following to create an account:

Now, using Xrm.WebApi this can be rewritten as:

Faking Web API calls using xrm-mock

XrmMockGenerator.initialise() initialises an empty Xrm.WebAPI object. Calls to its methods such as createRecord throw a not implemented error. The current recommended approach is to therefore stub any API methods being called in the code under test, and force their return values. This allows you to:

  • control your test's expected behaviour
  • prevent direct calls to the Dynamics database via XMLHttpRequest or similar

Here's an example

This example demonstrates a basic client-side script running on a Contact form in Dynamics. When the form is loaded, the script:

  • gets the Id of the Contact's Parent Contact via Xrm.Page.getAttribute.getValue
  • retrieves the Parent Contact's name via Xrm.WebApi.retrieveRecord
  • sets the Contact's description to "My parent is called {parent contact's name}" via Xrm.Page.getAttribute.setValue

This example uses Sinon.JS to help create Web Api stubs.

"Standalone and test framework agnostic JavaScript test spies, stubs and mocks (pronounced "sigh-non", named after Sinon, the warrior)."

First, here's contact.ts, the script which will be run on the Contact form:

And here's contact.test.ts, the script we'll use to test contact.ts:

Walkthrough: Understand the Code

Testing in action with Wallaby.js

And that's it

Using the example above you can see how to:

  • Create a fake Xrm object to use in client-side unit tests by using xrm-mock.
  • Stub a call to Xrm.WebApi to control your test's behaviour and not directly call the Dynamics database.
  • Write asynchronous TypeScript code for Dynamics.

This example and all its source code is available on xrm-mock's GitHub page here.

Automate UI testing for Dynamics 365 using EasyRepro and a VSTS Build Agent

Why test my Dynamics 365 UI?

We know how configurable Dynamics 365 is; so much so that no two Dynamics organisations are created equal. Companies work in different ways, leverage varying business processes and come in many shapes and sizes. This is great, because Dynamics is flexible enough to cope, and the developer community loves tackling new projects.

However, these very same companies are always changing, and so too are their Dynamics 365 organisations. This is where the developers come in; they configure each Dynamics organisation to meet their customer's needs, and this process is inevitably ongoing. Reconfiguring and adding to an ever evolving system comes with risk:

  • Do these new configurations meet the customer's specification?
  • Have these changes introduced regression to the existing system?

If your previous Dynamics customisations have tests written for them, these questions can all be answered during development.

As well as mitigating risks, UI tests come with benefits to enhance the usability of the Dynamics 365 organisation being configuring. This means happy stakeholders all around, from developers to end users.

One core benefit is enforcing form style guidelines. Should section headers all be in capital letters? Should the Created On field always be visible at the bottom of every form? Should form headers always contain four fields? Enforce it with UI testing!

What's automated UI testing?

UI tests that aren't automated, consist of a team reading through a list of behavioural expectations and then clicking through the Dynamics organisation manually and asserting a pass or fail to each expectation. These tests can be very click intensive and time-consuming, time that could be better spent by the QA team testing complex features end-to-end.

Automated UI testing is getting a computer to run these tests for you. Selenium is a popular tool built for this purpose: it automates web browsers allowing for websites to be tested by interacting with DOM elements.

Dynamics 365-specific commands have been written for us by Microsoft, in the open source project EasyRepro. In the project's own words:

The purpose of this library is to provide Dynamics customers the ability to facilitate automated UI testing for their projects. These API's provide an easy to use set of commands that make setting up UI testing quick and easy. The functionality provided covers the core CRM commands that end users would perform on a typical workday and working to extend that coverage to more functionality.

Ok, how do I test my Dynamics 365 UI?

Using EasyRepro! See its GitHub page for examples spanning forms, charts, navigation, dashboards and more. I've added an example here below which demonstrates a basic test of navigating through Dynamics and opening a contact form:

Notice how readable the functions of XrmBrowser are such as SwitchView and OpenRecord. This is how good tests should be: human readable and descriptive similar to a design document.

Running UI tests locally takes ages, how do I automate it?

Yes, you'll notice that when your UI tests are run (presumably in Visual Studio), a browser window appears as if by magic and your mouse starts moving to sign into Dynamics, and begin running each test individually. If only these tests could run else where and you could carry on with your work..

Well they can, at least, ever since this commit to GitHub which adds headless compatibility to EasyRepro. Headless Chrome is still just Chrome, but without the UI shell. This is necessary because VSTS Build Agents, which we'll be using to act as a remote machine to run our automated tests, don't have a UI for us to use. Chrome without Chrome!

So with these tools, we can configure a headless Chrome browser to run in a VSTS Build Agent, while we get on with other work.

How do I configure my VSTS Build task?

  1. Ensure your code is checked into VSTS along with your test code project
  2. Restore your NuGet packages and compile your code (as shown in the image below)
  3. Install Chrome silently (without installation prompts) on the build agent

  4. Run your UI tests by referencing their test assemblies

You'll also need to configure your EasyRepro Test Options

Your EasyRepro test methods require an XrmBrowser object, which itself requires a BrowserOptions object.

So, create your TestSettings class and specify Chrome as BrowserOptions.BrowserType:

Then pass your TestSettings.Options to XrmBrowser in your test classes:

And you're done, you're now equipped to automate your UI tests for Dynamics 365 both locally and from a VSTS Build Agent, using Microsoft's EasyRepro and Selenium.

If your current place of work doesn't make use of tests or UI testing, you now also have a basis of discussion to convince others that UI testing is worthwhile! (As if the incidence of regression bugs wasn't enough!)

Unit test your client-side customisations: xrm-mock v3 released!

Unit testing your client-side customisations for Dynamics 365 just got easier.

xrm-mock is a package that lets you generate a fake implementation of the Xrm object in your client-side code, allowing you to unit test your scripts.

How do I install it?

Simply download xrm-mock's npm package by running

npm install xrm-mock -D

Then import the XrmMockGenerator module and initialise a global Xrm object

import { XrmMockGenerator } from "xrm-mock";
XrmMockGenerator.initialise();

Now you have a global Xrm object, which you can add attributes to, to simulate a real form in Dynamics 365.

Here's how you add basic attributes:

Now for a lookup attribute:

And finally an option set attribute:

See xrm-mock's readme and Wiki for examples of how to unit test against your newly created attributes.

What's changed in v3?

As part of xrm-mock v3 release, attribute create methods only need two arguments: a schema name and a value, as shown in the above code snippet.

Why xrm-mock?

Faking Xrm is hard and time consuming: it has several nested objects which require a detailed understanding to fake correctly. xrm-mock is gaining traction with the open source community, and can expect to see more releases in the coming months. In fact, Daryl LaBar has become a contributor and he's already hit the ground running.

Unit test your client-side customisations even faster

Last month I released this post detailing how you can unit test your JavaScript customisations for Dynamics 365.

In summary, xrm-mock is an npm package which you can install to mock the Xrm object in your client-side code. By mocking Xrm, you can write tests to assert the state of your CRM forms after JavaScript functions are called, such as onLoad() and onChange() for a field.

xrm-mock has been used across several projects both by myself and others, and has subsequently received some useful feedback. Namely:

"mocking Xrm is hard"

...and not to mention time consuming.

Enter: xrm-mock-generator. This project drastically reduces the time it takes to mock your CRM form's Xrm object. A usage guide is available on the project's GitHub page (link here), but here it is again:

  • Step 1: install via npm install xrm-mock-generator
  • Step 2: import var XrmMockGenerator = require("xrm-mock-generator");
  • Step 3: initialise a global Xrm object var Xrm = XrmMockGenerator.initialise();
You now have a global Xrm object, as if you had loaded a form in CRM.

Here's an example:

This example showcases a contact form that changes the contact's firstname from Joe to Bob when the form is loaded.

src/contact.js

test/contact.test.js

You'll notice the line: XrmMockGenerator.createString("firstname", "Joe"); - that's how you add an attribute to Xrm. You can also add date attributes, option sets etc. which is all detailed on the GitHub usage guide.

The next primary goal of the project is to automate attribute metadata. That is: connect to a CRM organisation and automatically download the metadata for a form. If you feel like you can contribute or offer a suggestion, please message me on GitHub.

Unit test your client-side customisations

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:

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.

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: