jest-websocket-mock icon indicating copy to clipboard operation
jest-websocket-mock copied to clipboard

Running multiple jest tests after each other fails

Open Tockra opened this issue 1 year ago • 0 comments

Hi folks,

I've following tests:

Jest Test Code
import WS from "jest-websocket-mock";
import { act, cleanup, renderHook } from "@testing-library/react";

import { z } from "zod";
import useLiveDbHook from "./websockets";
const BASE_URL = "http://127.0.0.1:8080";

jest.mock("react-oidc-context", () => ({
  useAuth: () => {
    return {
      isLoading: false,
      isAuthenticated: true,
      user: { access_token: "test-token" },
    };
  },
}));

describe("useLiveDbHook", () => {
  let server: WS;

  beforeEach(() => {
    server = new WS(`${BASE_URL.replace(/^http/, "ws")}/endpoint`);
  });
  afterEach(() => {
    server.close();
    WS.clean();
  });

  test.only("elements will be updated if update was received", async () => {
    const { result } = renderHook(() =>
      useLiveDbHook(
        "/endpoint",
        z.object({ _id: z.string(), name: z.string(), age: z.number() }),
      ),
    );

    await server.connected;

    server.send(
      JSON.stringify({
        operation_type: "insert",
        object_id: "some-id",
        data: { _id: "some-id", name: "Peter", age: 23 },
      }),
    );

    expect(server).toReceiveMessage(JSON.stringify(["some-id"]));

    server.send(
      JSON.stringify({
        operation_type: "insert",
        object_id: "some-id2",
        data: { _id: "some-id2", name: "Günther", age: 24 },
      }),
    );

    expect(server).toReceiveMessage(JSON.stringify(["some-id", "some-id2"]));

    server.send(
      JSON.stringify({
        operation_type: "update",
        object_id: "some-id",
        data: { _id: "some-id", name: "Hans", age: 30 },
      }),
    );

    expect(result.current.elements.length).toBe(2);
    expect(result.current.elements).toContainEqual({
      _id: "some-id",
      name: "Hans",
      age: 30,
    });
    expect(result.current.elements).toContainEqual({
      _id: "some-id2",
      name: "Günther",
      age: 24,
    });

    expect(server).toReceiveMessage(JSON.stringify(["some-id2", "some-id"])); // (elements.map(elem => elem._id)
    await cleanup();
  });

  test.only("elements will not be effected if a update with not existing id was received", async () => {
    const { result } = renderHook(() =>
      useLiveDbHook(
        "/endpoint",
        z.object({ _id: z.string(), name: z.string(), age: z.number() }),
      ),
    );

    await server.connected;
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "insert",
          object_id: "some-id",
          data: { _id: "some-id", name: "Peter", age: 23 },
        }),
      );
    });
    expect(server).toReceiveMessage(JSON.stringify(["some-id"]));
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "insert",
          object_id: "some-id2",
          data: { _id: "some-id2", name: "Günther", age: 24 },
        }),
      );
    });
    expect(server).toReceiveMessage(JSON.stringify(["some-id", "some-id2"]));
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "insert",
          object_id: "some-id3",
          data: { _id: "some-id3", name: "Hans", age: 30 },
        }),
      );
    });
    expect(server).toReceiveMessage(
      JSON.stringify(["some-id", "some-id2", "some-id3"]),
    );

    expect(result.current.elements.length).toBe(3);
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "update",
          object_id: "NOT_EXISTING",
          data: null,
        }),
      );
    });

    expect(result.current.elements.length).toBe(3);
    expect(result.current.elements).toContainEqual({
      _id: "some-id",
      name: "Peter",
      age: 23,
    });
    expect(result.current.elements).toContainEqual({
      _id: "some-id2",
      name: "Günther",
      age: 24,
    });
    expect(result.current.elements).toContainEqual({
      _id: "some-id3",
      name: "Hans",
      age: 30,
    });
    expect(server).toReceiveMessage(
      JSON.stringify(["some-id", "some-id2", "some-id3"]),
    );
    await cleanup();
  });

Remove one .only and the remaing test will pass. If both are executed with .only one test will fail (see following logs): Logs:

Logs of the different test executions

Single run 1:

 PASS  src/hooks/db/websocket.unit.ts
  useLiveDbHook
    ✓ elements will be updated if update was received (33 ms)
    ○ skipped connects to WebSocket
    ○ skipped reconnect with WebSocket if connection was lost
    ○ skipped elements contains received data
    ○ skipped elements will not be effected if a update with not existing id was received
    ○ skipped elements will be deleted if delete was received
    ○ skipped elements will not be effected if a delete with not existing id was received
    ○ skipped setElements causes a subscription request containing ALL current ids

Single run 2:

PASS  src/hooks/db/websocket.unit.ts
  useLiveDbHook
    ✓ elements will not be effected if a update with not existing id was received (32 ms)
    ○ skipped connects to WebSocket
    ○ skipped reconnect with WebSocket if connection was lost
    ○ skipped elements contains received data
    ○ skipped elements will be updated if update was received
    ○ skipped elements will be deleted if delete was received
    ○ skipped elements will not be effected if a delete with not existing id was received
    ○ skipped setElements causes a subscription request containing ALL current ids

Together

FAIL  src/hooks/db/websocket.unit.ts
  useLiveDbHook
    ✓ elements will be updated if update was received (31 ms)
    ✕ elements will not be effected if a update with not existing id was received (11 ms)
    ○ skipped connects to WebSocket
    ○ skipped reconnect with WebSocket if connection was lost
    ○ skipped elements contains received data
    ○ skipped elements will be deleted if delete was received
    ○ skipped elements will not be effected if a delete with not existing id was received
    ○ skipped setElements causes a subscription request containing ALL current ids

  ● useLiveDbHook › elements will not be effected if a update with not existing id was received

    expect(WS).toReceiveMessage(expected)

    Expected the next received message to equal:
      "[\"some-id\",\"some-id2\"]"
    Received:
      undefined

    Difference:

      Comparing two different types of values. Expected string but received undefined.

      111 |     );
      112 |
    > 113 |     expect(server).toReceiveMessage(JSON.stringify(["some-id", "some-id2"]));
          |                    ^
      114 |
      115 |     server.send(
      116 |       JSON.stringify({

      at src/hooks/db/websocket.unit.ts:113:20
      at fulfilled (src/hooks/db/websocket.unit.ts:5:58)

  ● useLiveDbHook › elements will not be effected if a update with not existing id was received

    expect(WS).toReceiveMessage(expected)

    Expected the next received message to equal:
      "[\"some-id2\",\"some-id\"]"
    Received:
      undefined

    Difference:

      Comparing two different types of values. Expected string but received undefined.

      133 |     });
      134 |
    > 135 |     expect(server).toReceiveMessage(JSON.stringify(["some-id2", "some-id"])); // (elements.map(elem => elem._id)
          |                    ^
      136 |     await cleanup();
      137 |   });
      138 |

      at src/hooks/db/websocket.unit.ts:135:20
      at fulfilled (src/hooks/db/websocket.unit.ts:5:58)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 6 skipped, 1 passed, 8 total
Snapshots:   0 total
Time:        1.683 s
Ran all test suites.

Both tests are testing a react hook I wrote which connects to a websocket server. You can pass the endpoint name to the hook. The hook itself will use useAuth to determine the access token and add this to the websocket url : ws://<ip>/<endpoint>?token=<access_token>.

Further the hook provides a react state ( elements) and a setter (setElements). Everytime the websocket receives a message from the server, the elements can change (depending on the message). Everytime the elements change (covered by useEffect(() => ..., [elements]); inside of useLiveDbHook) the client sends a list of the _ids of the current elements to the server.

So if the server sends a element, then the elements change and the client answers with the old id list including the new element (if INSERTED).

I tested the logic manually and I'm sure it works. But the tests I build just fails if I execute them after each other but are passed if I run them separatly. I have no idea why...

Tockra avatar Apr 19 '24 17:04 Tockra