Recommendations for unit testing components from the UI Kit?

Are there any recommendations for implementing unit tests with Forge’s new UI Kit? This is easily done with a Forge Custom UI using React’s testing libraries, etc… Is there a comparable strategy for component testing with the UI Kit? (I’m assuming the React libraries don’t work with UI Kit…maybe I’m wrong? Sorry, I’m very new to this.)

Also, I fully understand that UI Kit is still in beta. Perhaps a component testing feature is on the road map?

Any insight is appreciated. The UI Kit is really coming along well, and I’m evaluating whether we can build our next app with it. Thanks so much!

2 Likes

Hi @AaronMorris,

Great question! I’m reaching out to the team for a recommendation here.

Will be back shortly.

1 Like

It’s taking a while :sweat_smile: - I was wondering the same, any way to test those UI kit components? @testing-library/react doesn’t seem to be working.

1 Like

Hey @DanielPlatas @AaronMorris,

Really sorry for not getting back to you. UI kit doesn’t have a very good testing story at the moment. We don’t have any official recommendations here yet, although this does seem to me like a key gap to fill as UI kit aims to get out of beta. I’ll raise this with the team and product manager to see where on the roadmap it fits.

That said, I just had a play around with jest, and managed to find a set up that I was able to use to write simple unit tests for Forge UI kit components.

You will need to add a babel.config.js:

module.exports = {
  presets: [
    ["@babel/preset-env", { targets: { node: "current" } }],
  ],
  plugins: [
    [
      "@babel/plugin-transform-react-jsx",
      {
        pragma: "ForgeUI.createElement",
      },
    ],
  ],
};

Run: npm install --save-dev jest babel-jest @babel/plugin-transform-react-jsx @babel/preset-env on the command line at the root to install the necessary dependencies.

You should then be able to write your tests and run them using jest. For example, I added a script to my package.json: "test": "jest".

Then when I wrote my index.test.js and ran npm run test, it ran the test successfully.


In order to test the UI kit component, you will need to import and use it as if it were a function. It will return a JavaScript object that represents the components it returns.

E.g.

if my index.js has:


export const App = ({ featureFlags }) => {
  if (featureFlags && featureFlags.text) {
    return <Text>Hello</Text>;
  }

  return null;
};

I can test this using:

const { App } = require("./index");

const result = App({ featureFlags: { text: true } });
// returns { type: 'Text', props: {}, children: [] };
expect(result.type).toBe("Text");

Unfortunately, to go further than one level of custom component logic, you will need to manually traverse the tree yourself (i.e. the children will contain a function with props you can then call with the props as the parameters).

The Forge team will likely need to provide some sort of @testing-library/react equivalent, but for UI kit.

1 Like

Hey @kchan, that’s a nice setup, thank you for the examples - I’m no expert when it comes to javascript configurations and this is a great help while waiting for that testing library, thanks a bunch!!
Edit: I was trying this but unfortunately found a major problem with this approach: if there are hooks in this component it will complain that “hooks cannot be used outside a component” :frowning:

Hey @DanielPlatas,

Ah that’s another key use case that will be best solved by some sort of testing library.

In the meantime, you can try mocking out the hooks.

For example, add this to the top of your test file:

const { useState } = require("@forge/ui");

jest.mock("@forge/ui", () => ({
  __esModule: true,
  ...jest.requireActual("@forge/ui"),
  useState: jest.fn(),
}));

Then you can add this before each test to mock what you want it to return:

  useState.mockImplementation(() => ["test state"]);

@kchan – Thank you for the follow-up, and the snippets are much appreciated!

hey @kchan, indeed I started investigating that and it is working fine, so it is a good workaround! Thanks for your time and help.

PD: I wasted some time on a problem originally and your snippet helped me solve it, so I might as well mention the situation to maybe help others stumbling into the same case - when using:

jest.mock("@forge/ui", () => ({
  __esModule: true,
  ...jest.requireActual("@forge/ui"),
  useState: jest.fn(),
}));

it is important the “__esModule: true” property, otherwise the result object has some problems creating the default property, needed to render the component!

Edit: just a question about your first post about the solution @kchan, you said that to traverse the children I’d have to do it manually but, I haven’t been able to produce a component that has anything in said children, i.e. I get my rendered component of type XXX, but when the componentes are other components I created instead of UI kit ones, the children are always empty despite having more components inside. I was using ts-jest instead of babel to achieve the same for this tests and it is working fine but I don’t know if it has something to do with it?

Example:

This component shows the children successfully:

const MyComponent = () => 
      <SectionMessage>
         <Text>some test</Text>
      </SectionMessage>

But this one has the correct type as “MySecondComponent” but the children are empty always:

// components.js
const MyComponent = () => {
    // operations ...
    return <MySecondComponent />;
}
const MySecondComponent = () => <Text></Text>;

//components.test.js
describe("tests", () => {
   test("children are empty", () => 
      const rendered = MyComponent();
      expect(rendered.type.name).toEqual("MySecondComponent"); // true
      // but I never get any children inside MySecondComponent
   })
});

Hey @DanielPlatas,

rendered.type should actually be the MySecondComponent function, so you will need do:

const secondComponentRendered = rendered.type(rendered.props);
const secondComponentChildren = secondComponentRendered.children;

This might sound a bit controversial but I found e2e testing of my UI Kit app more useful than unit tests. Keen to share details if someone’s interested.

Oh that’s awesome thanks @kchan!

It would be great to hear about your experience @Dmitrii if you feel like sharing it :slightly_smiling_face:

@DanielPlatas here’s a link to my app e2e tests.

The gist is: UI Kit apps are client-server apps and there’s an established protocol (JSON-based) that is used to communicate. All UI Kit Javascript is executed on the server. The server returns a JSON to the client and the client knows how to display this JSON. GraphQL API is used as a mechanism, therefore, we can emulate this GraphQL API request and check that the response JSON is expected.

Compared to this approach, we can also test UI Kit apps as if they are normal React apps. However, at the moment, they are not. Therefore, it definitely makes sense to unit test some logic inside your app but I don’t feel like it’s worth to test rendered UI (using @testing-library/react or enzyme).

To summarise this, pros and cons of UI Kit apps e2e testing as I see it now:

Pro: we’re testing what users will actually see
Con: UI Kit is in beta and the protocol can change
Con: e2e tests are generally more complicated to build and support

Pros and cons of UI Kit apps unit testing:

Pro: faster running tests, easier to build and support
Con: You only hope that it will behave like React but it’s not (UI tests)

4 Likes

@DanielPlatas , @kchan , and @Dmitrii – Thank you for all the questions, answers, and code examples! I’ve been watching and learning. :slight_smile:

1 Like