I lead a moderately large ReScript project (110k loc). As we have increased our team and more requirements are being developed, we have recently introduced testing.
But I face the problem of having to make some internal APIs public (expose them in .resi) to be able to unit-test some of the tricky implementation details. And LLMs, pick them up as actually public APIs and “reuse” them in unexpected places, creating unwanted coupling.
Ideally I would keep .resi files with only the public APIs. But then my tests need to always be done in terms of the observables of the API. This is not bad in itself, but some complex frameworks would benefit from smaller units of testing.
My proposal follows a similar “outlook” to the rust in-module tests with @test accompanied by a tool rescript test to collect and run them.
@test
module InternalPrivateTest {
@test
let test_some_internal_property: unit => unit = () => {
throw(AssertionError("..."))
}
}
I understand there will be challenges of many kinds: async tests, how to interface the rescript test with other test runners (maybe not at all), etc.
In any case, I think this is conversation worth having.
Small rust example
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn convert_f32_to_totals(a: f32, b: f32) {
let ta: Totalized<f32> = a.into();
let tb: Totalized<f32> = b.into();
assert_eq!(a.partial_cmp(&b).unwrap_or(Ordering::Equal), ta.cmp(&tb))
}
#[test]
fn convert_f64_to_totals(a: f64, b: f64) {
let ta: Totalized<f64> = a.into();
let tb: Totalized<f64> = b.into();
assert_eq!(a.partial_cmp(&b).unwrap_or(Ordering::Equal), ta.cmp(&tb))
}
#[test]
fn convert_totals_back_to_f64(a: f64) {
let ta: Totalized<f64> = a.into();
let b: f64 = ta.into();
assert!((a - b).abs() < 1e-10f64);
}
}
}