firebase / firebase-functions-test

MIT License
235 stars 53 forks source link

I does'nt can run test in onRequest http Cloud Functions V2 #208

Open ulisseshen opened 1 year ago

ulisseshen commented 1 year ago

Version info

firebase-functions-test:

  "devDependencies": {
    "@types/chai": "^4.3.5",
    "@types/mocha": "^10.0.1",
    "chai": "^4.3.7",
    "firebase-functions-test": "^3.1.0",
    "mocha": "^10.2.0",
    "typescript": "^4.9.0"
  },

firebase-functions: "firebase-functions": "^4.4.1",

firebase-admin: "firebase-admin": "^11.8.0",

Test case

//index.ts
import { onRequest, HttpsOptions } from "firebase-functions/v2/https";
const options: HttpsOptions = { maxInstances: 10, };

export const helloWorld = onRequest(options, (request, response) => {
  logger.info("Hello logs!", { structuredData: true });
  response.send("Hello from Firebase!");
});
//index.test.ts
import {helloWorld} from '../src/index';
import * as firebaseFunctionsTest from 'firebase-functions-test';
import { expect } from 'chai';

const testEnv = firebaseFunctionsTest();

describe('helloWorld', () => {
  it('Deve retornar "Hello from Firebase!"', async () => {
    // Crie um contexto simulado (mock) para a função
    const wrapped = testEnv.wrap(helloWorld);

    console.log({wrapped})

    // Chame a função simulada usando o contexto simulado
    const result = await wrapped({});

    // Verifique se o resultado está correto
    expect(result._body).is.equal('Hello from Firebase!');
  });
});

// Depois de escrever seus testes, lembre-se de limpar o ambiente de teste
testEnv.cleanup();

Steps to reproduce

This line const wrapped = testEnv.wrap(helloWorld); in the ´index.test.ts´ file give me a error that I not allow me continue with my tests.

following error.
    Argument of type 'HttpsFunction' is not assignable to parameter of type 'CloudFunction<unknown>'.
      Property 'run' is missing in type 'HttpsFunction' but required in type 'CloudFunction<unknown>'.
  Overload 3 of 3, '(cloudFunction: CloudFunction<CloudEvent<unknown>>): WrappedV2Function<CloudEvent<unknown>>', gave the following error.
    Argument of type 'HttpsFunction' is not assignable to parameter of type 'CloudFunction<CloudEvent<unknown>>'.
      Property 'run' is missing in type 'HttpsFunction' but required in type 'CloudFunction<CloudEvent<unknown>>'.ts(2769)
cloud-functions.d.ts(215, 5): 'run' is declared here.
core.d.ts(52, 5): 'run' is declared here.

Expected behavior

Let me run my test with http onResquest cloud functions (v2?)

Actual behavior

It doesn't allow me make tests on the http onResquest cloud functions

s-petey commented 9 months ago

@ulisseshen I don't believe wrap is supported for onRequest methods for firebase functions. So all helloWorld is in your test is a function you can trigger. Using TS you can "make it work" using whatever test framework you choose.

In this example I'm using vitest a fast test runner leveraging vite.

import { helloWorld } from '../src/index';
import * as firebaseFunctionsTest from 'firebase-functions-test';
import { describe, expect, it, vi, afterEach } from 'vitest';
import { onRequest } from 'firebase-functions/v2/https';

// So you aren't cluttering your console during tests I will 
// also recommend mocking out the logger method(s)
vi.mock('firebase-functions/logger');

type FbRequestFunction = Parameters<typeof onRequest>[0];
type FbRequest = Parameters<FbRequestFunction>[0];
type FbResponse = Parameters<FbRequestFunction>[1];

const testEnv = firebaseFunctionsTest();

afterEach(() => {
  // Depois de escrever seus testes, lembre-se de limpar o ambiente de teste
  testEnv.cleanup();
});

describe('helloWorld', () => {
  it('Deve retornar "Hello from Firebase!"', async () => {
    const mockedSendFunction = vi.fn();
    // Cast here as Firebase doesn't share any "mock response"
    const fakeResponse = {
      // Here within the response we must
      // mock out any `response` methods that are
      // called within the test itself such as
      // - status
      // - sendStatus
      // - send
      // - json
      // - contentType
      // - type
      // - redirect
      send: mockedSendFunction,
    } as unknown as FbResponse;
    // Cast here as Firebase doesn't share any "mock request"
    const fakeRequest = {
      // Here we can add anything that we would trigger on
      // the `request` such as
      // - headers
      // - method
      // - url
      // - statusCode
      // - statusMessage
    } as unknown as FbRequest;

    // Now we can trigger the function
    // Chame a função simulada usando o contexto simulado
    await helloWorld(fakeRequest, fakeResponse);
    // Note there is no return value of this method, because it exists
    // on the methods we mocked on the `fakeResponse` above.

    console.log(mockedSendFunction);

    // I don't know the specifics of chai, however using something
    // like vitest or jest you can look at the mocks.
    console.log(mockedSendFunction.mock.lastCall);

    // Verifique se o resultado está correto
    // then with vitest and jest based testing you can assert
    // against the mock
    expect(mockedSendFunction).toBeCalledTimes(1);
    expect(mockedSendFunction).toBeCalledWith('Hello from Firebase!');
    // Or if you are expecting an object you can use something like
    // expect(mockedSendFunction).toBeCalledWith(
    //   expect.objectContaining({
    //     message: 'Hello from Firebase!',
    //   })
    // );
  });
});

If you have any additional questions please feel free to let me know. I am not a maintainer of firebase, just ran into this same problem. Sadly it seems their docs and example are out of date and a lot of the links on their website point to repositories that no longer exist on github or have been moved. EX: https://firebase.google.com/docs/functions/unit-testing#testing_http_functions -> addMessage() example function link is broken.