What is your approach to test a ReasonReact binding?

reasonreact

#1

What is your approach to test a ReasonReact binding? I have some fairly complex bindings and I would like to test it.


#2

Do you mean the the wrapJsForReason/wrapReasonForJS calls? If so, you can just check them once right?


#3

You mean like with the method I’m doing, I can check the props generated by special create function?


#4

There are subtleties like making sure we spelled the polymorphic variants correctly if we’re using them to generate strings for JS components. E.g. [@bs.deriving jsConveter] type position = [`start | `end_]–we needed that string to be "end" but it will come out as "end_".


#5

Thanks. That is definitely what I want to test for.

But ideally, this is the public API I want to write my test in;

  1. Write the component in ReasonReact
  2. Write the component in ReactJS

Compare 1 and 2. This would serve as example as well.


#6

Yeah I guess bindings by themselves aren’t really amenable to automated testing. You can test the components you write with them as usual React components, but issues like the one I mentioned might be hard to track down, and it’s hard to test for everything. I wonder how other JS typing communities tackle this.


#7

This test the implementation detail of the original component IMHO, I only want to test if the binding is working correctly by passing props I wanted to ReactJS

They test by making sure it passes the type system
For example with Flow:


#8

The flow style tests are not really useful in this case IMO. Those tests are needed because flow types are very often quite complex with a lot of row polymorphism going on. Also, the core issue here is validating against the next version of the library and such tests don’t provide you that at all.

In my opinion we (probably) will have a tool which checks externals against existing Flow/TS declarations. The nice property there is that all externals are subtypes of the JS counterparts. Involving runtime here is not needed IMO and overcomplicates things.


#9

I think what should I hack on this weekend. A mock function for ReasonReact.wrapJsForReason that generate Typescript/Flow code for testing


#10

Wow, awesome! I think there is relatively enough effort put into type generation. If I were you, I’d assume that the generation part is done. For instance by hardcoding like this (take it with a grain of salt):

/* Flow: function load(x: SomeFlowType) => void */
[@bs.val] external load : jsType => unit = "";

Then the secret sauce, the tool would go through all those declarations and check if they are subtypes of corresponding Flow counterparts.

There’s a catch with this approach though.

/* Flow: ??? */
[@bs.send] external send : jsType => unit = "";

IIRC there’s no way to declare a method like this in Flow. So maybe the literal declaration in the comment is not a good format.

I’ll be happy to see what you come up with.

One last rambling semi-unrelated thought; in order to do this (at least I think it’s the only practical way) we need a way to encode that an OCaml type corresponds to a flow type.

EDIT: I’m not sure if I’m being clear. A bit sleep deprived today. Anyhow, what I mean is that type generation itself will be the icing on the cake but such tool would be useful even if we were to provide manual annotations to externals.


#11

I’m having something completely diifferent in mind. I want to codegen JSX based on ReasonML input and run the generated code with Typescript/Flow compiler.


#12

This very topic kept me up thinking the other night. I have a binding that I wanted to test, but all I could think of doing was clunky, brittle integration tests. There’s got to be a better way.

When I did bindings like this for a tool that generated JS from plain english, I had nimble unit tests in complete isolation. Maybe we could do something similar. Instead of testing the result of the generated code, I only tested that the generated code was expected. Similar to snapshot testing. For example:

test(“create new monkey results in new monkey()”, () => {
var english = “create new monkey”;
var expectedGeneratedCode = “new monkey()”;
var result = generator(english);
expectEquals(result, expectedGeneratedCode);
});

Something like this would be amazing (to me):

test(“execute dbInfo method”, () => {
let generated = BuckleScriptCompiler.getJavascript(() => Database.(dbInfo()));
let expectedOutput = “Database.dbInfo()”;
expectEquals(expectedOutput , generated );
});

If there is a way to access the generated js code from the test, then we could test each generated method in isolation. Is there a way to do this in ReasonML?