Inline tests in rescript

Hi,

Some tools in the native OCaml world allow to write inline tests, like ppx_inline_test, which in my opinion have a couple of very solid benefits:

  1. colocation of implementation and tests
  2. being able to test functions that are not exported.

Such tools exist in other languages, in Rust for example.

I know @chenglou was working on some testing framework for Rescript, is it a feature you were considering? Is it something that would interest people in the community?

10 Likes

Does this exist in JavaScript, that we could perhaps model a ReScript implementation on?

1 Like

I like how the educational language Pyret does it:

fun sum(l):
  cases (List) l:
    | empty => 0
    | link(first, rest) => first + sum(rest)
  end
where:
  sum([list: ]) is 0
  sum([list: 1, 2, 3]) is 6
end

just write a where clause after your function and put the tests in there.

I couldn’t find any equivalent in JS.

What I had in mind was a simple PPX that would not generate anything when building for prod or if it’s a dependency so that it would be 0 cost and inside of which you could use the test framework you want.

1 Like

this is a nice syntax indeed!

1 Like

Good idea. I like elixir inline tests (in commnets) too.
but it would be tricky in general since it should eval javascript.

Is there a way to avoid shipping the tests when publishing the a module? Also, when do the tests run? Is it on import? Is there any way to disable the tests from running?

I mean, there’s nothing really to stop you from doing this in your source files with your test framework of choice

if (process.env.NODE_ENV === "test") {
  describe("hi", () => {
    it("should be something", () => {
      expect(true).toBeTruthy();
    });
  });
}

Pretty sure most bundlers would drop it from the module at compile time

1 Like

yes sure, you could also probably use conditional compilation here if you don’t want to use a bundler, but then I guess it means any file can be a test file, which is not ideal, I thought the PPX could also take care of this so we wouldn’t have to point the tests to your whole src folder.

hey, so we decided it was worth it and took a few days to work on a tool that does that and we’re quite happy with the result!

Basically it’s test-framework agnostic solution that allows you do this:

// Math.res
// this function might not even be in Math.resi 
// so impossible to test otherwise
let sum = (a, b) => {
  a + b
}

// you can use any test framework you like here:
@inline_test
Jest.test("adds 1 + 2 to equal 3", () => {
  expect(sum(1, 2)).toBe(3);
});

When building for prod, the whole test will be skimmed from the output exactly as you’d want!

This is still beta but please try it and share your feedback if you like what you see!

12 Likes

Hi @tsnobip, I just try to setup your tool but compilation failed after adding the ppx

Error while running external preprocessor
Command line: inline-test-ppx

FAILED: subcommands failed.
>>>> Finish compiling(exit: 1)
// ...
"@dialo/inline-test-ppx": "^0.0.1-beta.3",
"rescript": "^10.1.0-alpha.2"

Any idea ?

Hey, can you come up with a minimal repro and create an issue on the repo? We’re using it with rescript 10.0.1 at work, maybe 10.1.0 alpha 2 brought a breaking change.

Ok, nothing to see with rescript, it seems their is a problem using yarn 3, everything work well using yarn 1 … I haven’t had time to look into it further, but I’ll do it when I can.

2 Likes

I know this topic is a bit old, but this fairly easy to add in Javascript by (ab)using labelled block statements.

let tips = [
  "a",

  "b",

  "c"
];

function printTips() {
  return tips.map((tip, i) => (`Tip ${i + 1}: ` + tip)).join(' | ');
}
where: {
  
  expect(typeof printTip()).toBe('string');
  
  expect(printTips())
    .toEqual(`Tip 1: a | Tip 2: b | Tip 3: c`);
}

There is simply a block labeled with where:. Since it’s a block and not a function, when this file is imported, the tests simply run. A compiler can be set up to find and remove labelled statements where the label is where:.

Better yet, I think it make sense to have a set of label names that can have this special meaning:

  • where: for short tests after function statements specifically
  • describe: for groups of tests. Labelled blocks can be nested
  • test

Rescript doesn’t have labeled blocks, but it should be possible to add something similar in ReScript as well.

1 Like