TheLuckyLoki
TheLuckyLoki6mo ago

Errors with mocking & testing in supabase edge function running on deno.

Hi everyone - I am sincerely hoping you can save me from pulling out all my hair with this. At my company we're using supabase, and want to use edge-functions, setting up this part was relatively quick and easy; however, I'm a test-fanatic, and I believe that nothing in our system should go without testing. Meaning, every edge function should have a decent test-coverage, this INCLUDES mocks/spies/stubs/fakes. The way supabase presents their testing docs, they assume we're always running on database, querying directly on a DB. However, good practices is to mock out our calls and return dummy data to assert whether the system can be proven to be robust in different situations. However, the native denoland test/mock setup: https://docs.deno.com/runtime/manual/basics/testing/mocking Does not allow me to effectively spy on or mock the supabase package. Example of how we setup our client: import {createClient} from 'https://esm.sh/@supabase/supabase-js'; const default supabase = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '', ); we then call "supabase.from('some-table).select();" and expect to get some info back. I'd love to be able to do something like: const fromStub = stub(supabase, "from", [{'some object data here'}])); and see the stubbed data returned. Sadly though, I can confirm that the stub/spy is never called within the production. I have spent a good 10 hours researching this subject, and have tried many approaches including import_maps and third party libs like sinonJS. https://stackoverflow.com/questions/69677679/deno-mock-out-named-import-in-unit-test This is incredibly frustrating, and if any of you have tried this use-case or similar, I'd appreciate some input. The current documentation at supabase is severely lacking on the subject of mocking/spying/stubbing and it appears that something in the supabase setup makes it not work directly as presented in the deno docs.
4 Replies
marvinh.
marvinh.6mo ago
Mocking ESM modules in general regardless of the runtime is not as straightforward as with CommonJS where the internal module record can be messed with at will. That's not doable with ESM and tools that partially support that like jest rely on a crazy amount of custom transpilation behind the scenes to make that work. You mentioned that you want to mock DB queries and for that module mocking is not needed. We could just mock the DB instead. Basic example:
import {createClient} from 'https://esm.sh/@supabase/supabase-js';

- export default supabase = createClient(
+ let original;
+ let supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
);

+ export default supabase;
// Pseudo code, real mock in your app might look different
+ export function mock() {
+ original = supabase;
+ supabase = new MyMockSubabase();
+ }
+
+ export function restore() {
+ supabase = original;
+ original = undefined;
+ }
import {createClient} from 'https://esm.sh/@supabase/supabase-js';

- export default supabase = createClient(
+ let original;
+ let supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
);

+ export default supabase;
// Pseudo code, real mock in your app might look different
+ export function mock() {
+ original = supabase;
+ supabase = new MyMockSubabase();
+ }
+
+ export function restore() {
+ supabase = original;
+ original = undefined;
+ }
TheLuckyLoki
TheLuckyLoki6mo ago
so - the main issue is that the supabaseClientJS is a more complicated package that does not allow us to listen in? The solution is therefore to "swap" the entire package we're running on. this is what I tried doing with import_maps, https://stackoverflow.com/questions/69677679/deno-mock-out-named-import-in-unit-test, and would've been ok with this. However, setting up the import_map in the test env, and calling the tests with a custom map, did not swap the import_map in the code I was testing. Though having learned more since that test I might realize the mistake. in order to simplify my test-suite I created a main.ts for my tests, that looks like this: import testClientCreation from "./client_creation/index.ts" import testCollectInvoices from "./collect_invoices/index.ts" // Register and run the tests Deno.test('Client_Creation Test', testClientCreation) Deno.test('collect_invoices Test', testCollectInvoices) and I would run the test with the following command. deno test --allow-all functions/tests/main.ts --env=.env.local --import-map=functions/tests/index.importmap.json is it possible that the Deno.test(description, testFile) weren't receiving the replaced import_map ? in this solution I created a supabase.mock.ts file that had a sinon solution similar to the provided stack overflow link; the goal being to replace supabase entirely while the testing was happening.
marvinh.
marvinh.6mo ago
To my knowledge the import map cannot be modified during runtime
TheLuckyLoki
TheLuckyLoki6mo ago
I'm gonna have to do some more thinking & research, thank you for your time and input.