Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing gRPC functions

I have a task to test gRPC client call functions with Jest. Here is what a typical node.js function looks like:

client.authenticate(request, meta, (error, response) => {
   if (!error) {
      console.log('REPLY FROM SERVER: ', response)
   } else {
    console.error(error)
  }
})

Procedure calls are callback functions, as we see I can not export response object to outside variable. The above function is the one I need to test. I need to check if the function has been called with no error. How do I do it with jest? Been struggling for a while now.

like image 244
Aika Sat Avatar asked Apr 13 '26 08:04

Aika Sat


2 Answers

You can use jest.spyOn(object, methodName) to mock client.authenticate.

E.g.

index.ts:

import { client } from './client';

export function main() {
  const request = {};
  const meta = {};
  client.authenticate(request, meta, (error, response) => {
    if (!error) {
      console.log('REPLY FROM SERVER: ', response);
    } else {
      console.error(error);
    }
  });
}

client.ts:

export const client = {
  authenticate(request, meta, callback) {
    console.log('real implementation');
  },
};

index.test.ts:

import { main } from './';
import { client } from './client';

describe('62214949', () => {
  it('should log correct response', () => {
    const mResponse = 'mocked response';
    const logSpy = jest.spyOn(console, 'log');
    jest.spyOn(client, 'authenticate').mockImplementationOnce((request, meta, callback) => {
      console.log('mocked implementation');
      callback(null, mResponse);
    });
    main();
    expect(logSpy).toBeCalledWith('REPLY FROM SERVER: ', 'mocked response');
    expect(client.authenticate).toBeCalledWith({}, {}, expect.any(Function));
  });

  it('should handle error', () => {
    const mError = new Error('network');
    const logSpy = jest.spyOn(console, 'error');
    jest.spyOn(client, 'authenticate').mockImplementationOnce((request, meta, callback) => {
      console.log('mocked implementation');
      callback(mError);
    });
    main();
    expect(logSpy).toBeCalledWith(mError);
    expect(client.authenticate).toBeCalledWith({}, {}, expect.any(Function));
  });
});

unit test result with coverage report:

 PASS  stackoverflow/62214949/index.test.ts (10.557s)
  62214949
    ✓ should log correct response (23ms)
    ✓ should handle error (8ms)

  console.log
    mocked implementation

      at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)

  console.log
    REPLY FROM SERVER:  mocked response

      at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)

  console.log
    mocked implementation

      at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)

  console.error
    Error: network
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/62214949/index.test.ts:18:20)
        at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
        at resolve (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:45:12)
        at new Promise (<anonymous>)
        at mapper (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
        at promise.then (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:75:41)
        at process._tickCallback (internal/process/next_tick.js:68:7)

       8 |       console.log('REPLY FROM SERVER: ', response);
       9 |     } else {
    > 10 |       console.error(error);
         |               ^
      11 |     }
      12 |   });
      13 | }

      at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
      at stackoverflow/62214949/index.ts:10:15
      at Object.<anonymous> (stackoverflow/62214949/index.test.ts:22:7)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |      90 |      100 |   66.67 |      90 |                   
 client.ts |      50 |      100 |       0 |      50 | 3                 
 index.ts  |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        12.424s
like image 56
slideshowp2 Avatar answered Apr 14 '26 22:04

slideshowp2


I got a very similar problem: we use Cypress to test a React App which uses GRPC-Web protocol to communicate with GRPC backends.

Almost every library out there to mock GRPC backends requires starting a full server along with your testing suite, which is pretty inconvenient and annoying in CI environments.

So I decided to make a small package that stimulates the part of the backend network which is responsible for encoding responses for gRPC-web clients. We use it in combination with Cypress and its network interception feature. So we can intercept a GRPC call and inject a mocked response during E2E tests.

https://www.npmjs.com/package/@botchris/grpc-web-mock

like image 38
Chris Castro Avatar answered Apr 14 '26 22:04

Chris Castro



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!