Testing TypeScript Server Runtime Code with Jest
This guide shows you how to configure and write tests for your TypeScript server runtime code using the popular Jest JavaScript Testing framework.
Writing tests for your code ensures that your code behaves as expected and safe guards you from making breaking changes in the future without realizing.
Configuring your TypeScript server project for Jest #
To write Jest tests you need to install a few different packages.
| |
In your tsconfig.json file add the following to your compilerOptions section:
| |
In your package.json add a new "test" script:
| |
Also add a new section for "jest":
| |
Finally create a jest-config.ts in the root of your project (or the path specified in the "setupFiles" section of your config).
| |
Writing a testable RPC #
This is an example RPC that a player might call to add an item to their inventory. The inventory in this case is an entry in the Nakama Storage Engine.
The code will check the incoming payload to ensure it meets specific criteria and at all potential failure points the RPC will log the error and return an appropriate failure response to the user.
Create a file called rpc-add-item.ts.
| |
Writing the first test #
Create a file called rpc-add-item.test.ts with the following imports.
| |
These imports allow you to create mocks of the Nakama server runtime types and functions. This allows you to test your code in isolation and under carefully crafted scenarios, without interacting with Nakama.
Create a describe block which allows you to group together related tests. Inside, define variables for the various mock objects we need to call our RPC.
| |
Next create a beforeEach block. The function you pass in will be run before each test. This is useful if you need to do some setup before a test. Here you will use this to configure the mock objects you defined earlier.
| |
The createMock function takes a generic type using the <> syntax to determine what type the mock should be. It will then proceed to create an object of that type with default/empty values for all of that type’s properties. You can override specific property values by passing an object into the function, such as the userId property above.
You will notice that you configured 3 specific functions to act as Jest mock functions (using the On function made available via ts-auto-mock/extension): logger.error, nk.storageRead and nk.storageWrite.
This is so that you can verify that these functions have been called with specific values depending on the test scenario.
Now you will write your first test. This test will check to make sure that the RPC returns a failure response with a specific error message when the payload passed to it is null.
| |
Here you:
- Call the
AddItemRpcfunction, passing in the mocked objects you defined andsnullfor the payload - Get the result as a JSON object using
JSON.parse - Define the expected error response string
Using the expect function you then verify that the:
resultPayload.successvalue isfalseresultPayload.errorvalue is the expected error string'no payload provided'logger.errorfunction was called with the same error message
To run your tests:
| |
If you run this test you should see that it passes.
You can verify that this test is indeed working by changing the expectedError value to something else and re-running the test. For example, changing the value to 'payload provided' will result in the following Jest output.
| |
Mocking the return value of Nakama functions #
Often when writing tests you will want to mock the return value of a particular function in order to change the outcome of the code being tested. With Jest this is possible through the use of various mockReturnValue functions.
In the following test, the mockReturnValueOnce function is used on the mockNkStorageWrite (as a jest.Mock type) function to force it to return null. This in turn means that the AddItemRpc function should fail and return a failure response.
| |
The mockReturnValueOnce function can be used to return any value from the mocked function and can therefore be used to completely change the result of the test.
Consider the following two examples:
| |
| |
In the first, the response of the nk.storageRead call is mocked to return no results, effectively emulating a response from Nakama that no storage objects exist for the specified collection, key, and user.
The second returns an array with a single object, indicating that the player’s current inventory contains 3 Diamond Pickaxes.
This is useful as it allows you to test how the RPC behaves when the player’s inventory is in different states.
Full example #
Below is the full code for the rpc-add-item.test.ts file covering various different success and failure scenarios.
| |
