D365 TypeScript Web Resources - Part 5 - Unit Testing
Before you get stuck into this make sure you’ve checked out any previous parts to the series. Each part in this series follows on from the previous, so you may need to grab the code from the previous part if you haven’t been following.
Unit Tests? But my code is next level!
Nowadays there is no excuse to not unit test your code. It’s becoming easy and more valuable, so don’t not do it!
In this post I’m going to provide a basic guide to get you started with unit tests and TypeScript webresources for Dynamics 365… err Dataverse… err Power Apps… or whatever we are calling it today!
Configure Jest
First of all lets install the jest npm packages we require:
1 | npm install jest @types/jest ts-jest --save-dev |
Next we’ll create a basic config via:
1 | npx ts-jest config:init |
Finally we’ll install xrm-mock and sinon to help us a little:
1 | npm install xrm-mock sinon @types/sinon --save-dev |
Ensure your package.json
is setup to run jest via npm run test
makes sure you have the following script defined:
1 | "scripts": { |
Now we are ready to write some tests! :-D
Writing our first test
Create a folder in the root of the project called “tests” and then create a new file called “first.test.ts”.
Paste the following into the new file:
1 | import { NavigationStaticMock, XrmMockGenerator } from "xrm-mock"; |
Lets have a closer look at the test…
I’m not going to tell you how to use jest and sinon as these already have great documentation of their own.
First we’ll “Arrange” our test…
Initialise our global Xrm
object:
1 | XrmMockGenerator.initialise(); |
Stub openAlertDialog()
. As I expect you know, openAlertDialog()
displays a dialog in D365. Stubbing the function enables the code to execute without error given we don’t have the UI and we can then test/assert the stubs properties etc.:
1 | const stub = sinon.stub(NavigationStaticMock.prototype, "openAlertDialog"); |
Now lets “Act” by calling our function:
1 | const msg = "a pointless test message"; |
Finally we’ll “Assert” our tests
Has the openAlertDialog()
stub been called?:
1 | expect(stub.called).toBeTruthy(); |
The openAlertDialog()
stub should have only been called once:
1 | expect(stub.calledOnce).toBeTruthy(); |
Was the openAlertDialog()
stubs alertStrings.text
parameter as expected?:
1 | expect(stub.firstCall.args[0].text).toBe(msg); |
Was the openAlertDialog()
stubs alertStrings.title
parameter as expected?:
1 | expect(stub.firstCall.args[0].title).toBe("A Pointless Message"); |
It’s quite a rudimentary test but it demonstrates the basics.
So, lets run the tests!
Execute npm run test
The output should look something like this:
1 | > jest |
Fancy a crack at mocking the WebApi?
So, we’ve written a function that calls Xrm.WebApi, but when we try to test that function it’ll fail as the Api doesn’t exist… We need to mock the Api call with a stub.
A function that calls Xrm.WebApi…
First of all we’ll create a function called CreateAccount
(yep you guessed it, it’ll create an account!)
1 | export async function CreateAccount(account: any): Promise<Xrm.Lookup> { |
It’s a simple example but it should get the point across.
Testing a function that calls Xrm.WebApi
So here’s the test. This can be added to the first.test.ts
inside describe("sample test", () => {
It’s not a great example, but it should demonstrate how we can stub the WebApi ;-)
1 | describe("CallTheWebApi", () => { |
You’ll notice the absence of XrmMockGenerator.initialise();
. I moved this to the beforeEach
within describe("sample test", () => {
like so
1 | describe("sample test", () => { |
Lets have a closer look again
First we stub the createRecord
function via the WebApiMock
object provided by xrm-mock
1 | const stub = sinon.stub(WebApiMock.prototype, "createRecord"); |
Then we define the arguments for that stub (optional). Notice the use of the sinon.match
to match an object.
1 | .withArgs("account", sinon.match.object) |
NOTE If we didn’t do the above the stub would be executed for all calls to Xrm.WebApi.createRecord
Finally we tell the stub what we would like it to return, or in this case as it’s a async/promise we tell it what to resolve.
1 | .resolves({ |
OK OK! Lets run the bloody tests!
Execute npm run test
and you should now get something similar to the following output
1 | > jest |
That’s all folks!
Remember to take a look at jest, sinon, and xrm-mock
I hope that has been useful!
You can download a copy of the source code for this blog post here
In the next part we’ll take a look at how we can integrate Azure Application Insights into the Webresources.
Thanks for reading.
Ollie