Search code examples
react-nativetestingjestjsautomated-testsrealm

Snapshot Testing - Realm already opened on current thread with different schema


I have made a React Native app which uses the Realm database. Opening this app and testing it manually does not result in any errors.

I have written tests for the Realm database, such as:

    test("Create record in example table", () => {
        realm.write(() => {
            realm.create("ExampleTable", { "id": 1, "name": "Example", "notes": "Example Notes" });
        });
        const exampleRecord = realm.objects("ExampleTable")[0];
        expect(exampleRecord.id).toBe(1);
        expect(exampleRecord.name).toBe("Example");
        expect(exampleRecord.notes).toBe("Example Notes");
    });

These tests pass.

I have also written snapshot tests for different screens:

test("index.js Test", async () => {
    const snapshot = renderer.create(<App />);
    const snapshotJSON = snapshot.toJSON();
    expect(snapshotJSON).toMatchSnapshot();
    snapshot.unmount();
});

These tests fail. The error is:

Realm at path '/home/runner/work/app-name/app-name/default.realm' already opened on current thread with different schema.

In order to fix the error, I have attempted to write multiple different lines in the beforeEach() and afterEach() code blocks of the test file. However, these have not worked. For example, I have written the lines jest.clearAllMocks(); and Realm.delete() in the afterEach() code block. Through research, I have found the error may be because the tests are run in conjunction and Realm does not allow this. However, I have found no way to attempt to run the tests individually.

The only errors I have gotten besides these errors are:

  ● Console
    console.error
      The above error occurred in the <CreateWorkout> component:
      
          at CreateWorkout (/home/runner/work/fitness-logger/fitness-logger/app/screens/create-workout/create-workout.js:10:63)
      

      113 |     const snapshotJSON = snapshot.toJSON();
      114 |     expect(snapshotJSON).toMatchSnapshot();
    > 115 |     snapshot.unmount();
          |              ^

To run the tests, I am using the command: npx jest --runInBand --detectOpenHandles --forceExit

Through research, I have found that I may need to mock the Realm database, although I am unsure how to do this. I am not using any Realm functions in my test file, although screens that I am taking snapshot tests of use the Realm database and use Realm functions

I have written the following mock in the test file:

jest.mock("realm", () => {
    const mockObjects = jest.fn(() => {
        const results = [];
        results.filtered = jest.fn(() => { return results; });
        return results;
    });
    function MockRealm() {
        return {
            "objects": mockObjects,
            "write": jest.fn(),
            "create": jest.fn(),
            "deleteAll": jest.fn(),
            "close": jest.fn(),
            "addListener": jest.fn(),
            "removeListener": jest.fn(),
        };
    }
    
    MockRealm.open = jest.fn();
    MockRealm.schema = [
        {
            "name": "Table",
            "properties": {
                "id": "int",
                "property": "string",
            },
            "primaryKey": "id",
        }
    ];
    MockRealm.deleteRealmIfMigrationNeeded = true;
    return MockRealm;
});

This stops the tests from failing. However, the tests still return the error from my try catch block: Failed to get data from schema. Moving this mock code into its own file in the __mocks__ directory causes the tests to fail.


Solution

  • Mock Realm in the testing file using the following code:

    jest.mock("realm", () => {
        const mockRealm = {
            "objects": jest.fn(() => {
                const results = [];
                results.filtered = jest.fn(() => { return results; });
                results.map = jest.fn(() => { return []; });
                return results;
            }),
            "write": jest.fn((callback) => { return callback(); }),
            "create": jest.fn(),
            "deleteAll": jest.fn(),
            "close": jest.fn(),
            "addListener": jest.fn(),
            "removeListener": jest.fn(),
        };
        mockRealm.open = jest.fn().mockResolvedValue(mockRealm);
        mockRealm.schema = [
            {
                "name": "Table",
                "properties": {
                    "id": "int",
                    "property": "string",
                },
                "primaryKey": "id",
            }
        ];
        mockRealm.deleteRealmIfMigrationNeeded = true;
        return jest.fn(() => { return mockRealm; });
    });
    

    Write the snapshot tests using act from react-test-renderer:

    test("index.js Test", async () => {
        const snapshot = renderer.create(<App/>);
        let snapshot;
        await act(async () => {
            snapshot = renderer.create(<App/>);
        });
        const snapshotJSON = snapshot.toJSON();
        expect(snapshotJSON).toMatchSnapshot();
        snapshot.unmount();
    });