diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts
index d08684243f8986cea7e90cb9de27b89bf5a65aba..9b24e932954b51627395339d1e29c9b2484e10f0 100644
--- a/packages/core/src/storage/FileSystem.ts
+++ b/packages/core/src/storage/FileSystem.ts
@@ -39,9 +39,24 @@ export class InMemoryFileSystem implements GenericFileSystem {
   }
 }
 
+export function getNodeFS(): GenericFileSystem {
+  const fs = require('fs/promises');
+  return {
+    exists: async (path: string) => {
+      try {
+        await fs.access(path);
+        return true;
+      } catch {
+        return false;
+      }
+    },
+    ...fs
+  }
+}
+
 let fs = null;
 try {
-  fs = require("fs");
+  fs = getNodeFS();
 } catch (e) {
   fs = new InMemoryFileSystem();
 }
diff --git a/packages/core/src/tests/InMemoryFileSystem.test.ts b/packages/core/src/tests/InMemoryFileSystem.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac09d816448810567dc880a3e8997f88dae7a442
--- /dev/null
+++ b/packages/core/src/tests/InMemoryFileSystem.test.ts
@@ -0,0 +1,90 @@
+import { GenericFileSystem, getNodeFS, InMemoryFileSystem } from "../storage/FileSystem";
+import os from 'os';
+import path from 'path';
+
+type FileSystemUnderTest = {
+  name: string,
+  prepare: () => Promise<any>,
+  cleanup: () => Promise<any>,
+  implementation: GenericFileSystem,
+  tempDir: string
+};
+
+const nodeFS = getNodeFS() as GenericFileSystem & any;
+
+describe.each<FileSystemUnderTest>([
+  {
+    name: 'InMemoryFileSystem',
+    prepare: async () => {},
+    cleanup: async function() {
+      this.implementation = new InMemoryFileSystem();
+    },
+    implementation: new InMemoryFileSystem(),
+    tempDir: './'
+  },
+  {
+    name: 'Node.js fs',
+    prepare: async function() {
+      this.tempDir = await nodeFS.mkdtemp(path.join(os.tmpdir(), 'jest-'));
+    },
+    cleanup: async function() {
+      await nodeFS.rm(this.tempDir, { recursive: true });
+    },
+    implementation: nodeFS,
+    tempDir: './'
+  }
+])("Test %s", (testParams) => {
+  let testFS: GenericFileSystem;
+  let tempDir: string;
+
+  beforeEach(async () => {
+    await testParams.prepare();
+    testFS = testParams.implementation;
+    tempDir = testParams.tempDir;
+  });
+
+  afterEach(async () => {
+    await testParams.cleanup();
+  });
+
+  test("initializes", () => {
+    expect(testFS).toBeTruthy();
+  });
+
+  describe("writeFile", () => {
+    it("writes file to memory", async () => {
+      await testFS.writeFile(`${tempDir}/test.txt`, "Hello, world!");
+      expect(await testFS.readFile(`${tempDir}/test.txt`, "utf-8")).toBe("Hello, world!");
+    });
+
+    it("overwrites existing file", async () => {
+      await testFS.writeFile(`${tempDir}/test.txt`, "Hello, world!");
+      await testFS.writeFile(`${tempDir}/test.txt`, "Hello, again!");
+      expect(await testFS.readFile(`${tempDir}/test.txt`, "utf-8")).toBe("Hello, again!");
+    });
+  });
+
+  describe("readFile", () => {
+    it("throws error for non-existing file", async () => {
+      await expect(testFS.readFile(`${tempDir}/not_exist.txt`, "utf-8")).rejects.toThrow();
+    });
+  });
+
+  describe("exists", () => {
+    it("returns true for existing file", async () => {
+      await testFS.writeFile(`${tempDir}/test.txt`, "Hello, world!");
+      expect(await testFS.exists(`${tempDir}/test.txt`)).toBe(true);
+    });
+
+    it("returns false for non-existing file", async () => {
+      expect(await testFS.exists(`${tempDir}/not_exist.txt`)).toBe(false);
+    });
+  });
+
+  describe("mkdir", () => {
+    it("creates directory if it doesn't exist", async () => {
+      await testFS.mkdir(`${tempDir}/testDir`);
+      expect(await testFS.exists(`${tempDir}/testDir`)).toBe(true);
+    });
+  });
+});
diff --git a/packages/core/test.txt b/packages/core/test.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5dd01c177f5d7d1be5346a5bc18a569a7410c2ef
--- /dev/null
+++ b/packages/core/test.txt
@@ -0,0 +1 @@
+Hello, world!
\ No newline at end of file