Unable to test mocked function using `fn` or `mocked`
Elliot-Evans-95 opened this issue ยท 6 comments
Hello,
I have been trying to mock my 3rd party functions in a fresh handler and I have used createHandlerContext
test my handler.
I am able to get back fields such as params
and status
as expected but the value are not what I would expect and I think there is a missing step in my test setup.
Take this example here:
describe("Flags Page", () => {
const viewPageUrl = `http://localhost:8000/flags/view`;
it("return a status of 200 via GET method", async () => {
const req = new Request(viewPageUrl);
const ctx = createHandlerContext(req, { manifest });
const res = await handler.GET!(req, ctx);
assertEquals(ctx.params, {});
assertEquals(res.status, 200);
});
});
Here the status will not return 200
because in the handler is a auth check. I want to mock out that auth check as its a 3rd party auth. So I am now adding this:
const mockedGetSessionId = mocked(getSessionId)
mockedGetSessionId.mockReturnValue("fake-session-id");
Therefore I would expect it to now work and be a 200 but it doesn't work. I think there is a missing step where in the jest world I would have to mock out the module in order for the mock function to work as expected, example:
jest.mock('kv_oauth', () => {
// would require actual here then mock out the function or have a hoist function that I can change per test
getSessionId: jest.fn().mockReturnValue("fake-session-id")
});
but Jest doesn't support ES Modules and even with jest.unstable_mockModule
it won't work as it would require the Jest runner to run the test and then the Jest setup object too.
Am I missing something here or am I right in thinking there needs to be a jest.unstable_mockModule
available?
Many thanks for the library and I hope you the above makes sense!
Can you point to a repo that includes the code-under-test and your test code including the imports?
@Elliot-Evans-95 Thanks for reporting the issue! There may be several workarounds for it.
1. Use spyOn()
to stub KV OAuth's helpers
First, prepare a module that exposes helpers
:
// kv_oauth_helpers.ts
import {
createGitHubOAuthConfig,
createHelpers,
} from "kv_oauth";
export const helpers = createHelpers(
createGitHubOAuthConfig(),
);
In the handler code, use helpers
via the above module:
// routes/some_route.ts
import { helpers } from "./path/to/kv_oauth_helpers.ts";
import { Handlers } from "$fresh/server.ts";
export const handler: Handlers = {
async GET(req, ctx) {
const maybeSessionId = await helpers.getSessionId(req);
if (maybeSessionId) {
// ..
} else {
// ...
}
},
};
Then the test code can stub getSessionId()
with spyOn()
as follows:
import { spyOn } from "$fresh-testing-library/expect.ts";
import { afterAll } from "$std/testing/bdd.ts"
import { helpers } from "./path/to/kv_oauth_helpers.ts";
import { handler } from "./path/to/routes/some_route.ts";
describe("Flags Page", () => {
const viewPageUrl = `http://localhost:8000/flags/view`;
const mockedGetSessionId = spyOn(helpers, "getSessionId").mockReturnValue("fake-session-id");
afterAll(() => mockedGetSessionId.mockRestore());
it("return a status of 200 via GET method", async () => {
// ...
});
});
2. Use KV OAuth's helpers via ctx.state
If you want to test without mocking/stubbing, you can also use helpers
via fresh's ctx.state
. First, prepare a middleware that configures ctx.state
:
// routes/_middleware.ts
import { MiddlewareHandlerContext } from "$fresh/server.ts";
import {
createGitHubOAuthConfig,
createHelpers,
} from "kv_oauth";
export const helpers = createHelpers(
createGitHubOAuthConfig(),
);
export interface State {
oauth: typeof helpers;
}
export async function handler(
req: Request,
ctx: MiddlewareHandlerContext<State>,
) {
ctx.state.oauth = helpers;
const resp = await ctx.next();
return resp;
}
In the handler code, You can call getSessionId()
via ctx.state
:
// routes/some_route.ts
import { helpers } from "./path/to/kv_oauth_helpers.ts";
import { Handlers } from "$fresh/server.ts";
import type { State } from "./_middleware.ts";
export const handler: Handlers<void, State> = {
async GET(req, ctx) {
const maybeSessionId = await ctx.state.oauth.getSessionId(req);
if (maybeSessionId) {
// ..
} else {
// ...
}
},
};
In the test code, ctx.state
can be set using state
option of createHandlerContext
:
import type { State } from "./path/to/routes/_middleware.ts";
import { handler } from "./path/to/routes/some_route.ts";
describe("Flags Page", () => {
const viewPageUrl = `http://localhost:8000/flags/view`;
it("return a status of 200 via GET method", async () => {
const req = new Request(viewPageUrl);
const state: State = { oauth: createFakeKvOAuthHelpers() };
const ctx = createHandlerContext<void, State>(req, { manifest, state });
const res = await handler.GET!(req, ctx);
assertEquals(ctx.params, {});
assertEquals(res.status, 200);
});
});
Hello @uki00a
Wonderful news, using your example of spyOn()
as a springboard I was able to get this working.
The big difference is the package versions:
"kv_oauth" - from "0.9.1" to "0.10.0"
"fresh-testing-library" - from "0.11.1" to "0.12.0"
After the above changes I am able to use this:
const mockedGetSessionId = spyOn(oauth2Client, "getSessionId").mockResolvedValue("fake-session-id");
The example from the docs helps to better understand how to use the createHelpers
from your example:
https://deno.land/x/deno_kv_oauth@v0.10.0/lib/create_helpers.ts?source
In addition apologies for the late response, happy for this to be closed ๐
I'm glad to hear it worked well ๐