Quickly testing the code I write

Hey fellow Rustaceans!

In the directory of my Ruby on Rails project I can run rails c and then run any statement. For example: calling a public method I've just written. Is there anything similar for Rust? It would make quick sanity testing so much easier!

Even if something like that isn't possible, I'd still love to hear some tips on how to run quick tests on new code I write.


P.S.:

Here's a real situation I've had today. I've added a new function in my_module:

use nalgebra::Vector2;

pub fn vector2_iter(from: Vector2<i64>, to: Vector2<i64>) -> impl Iterator<Item = Vector2<i64>> {
    (from.x..=to.x).flat_map(move |x| (from.y..=to.y).map(move |y| Vector2::new(x, y)))
}

The rust-analyzer didn't complain but I wanted to ensure the function did what I wanted it to do. With the Rails console functionality it would have been as easy as running:

crate::my_module::vector2_iter(nalgebra::Vector2::new(0, 0), nalgebra::Vector2::new(1, 1)).collect::<Vec<_>>())

And hoping to see [[[0, 0]], [[0, 1]], [[1, 0]], [[1, 1]]] outputted. It would have taken 5 seconds. But instead I had to write a test that I'll only use once:

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test() {
    println!("{:?}", vector2_iter(Vector2::new(0, 0), Vector2::new(1, 1)).collect::<Vec<_>>());
  }
}

Of course, with LLM-powered code completion, this didn't take that long. But it still took longer than simply running the statement in a console. Then in VSCode I simply clicked a button to run it (this saved time over typing cargo test -- --exact my_module::test)

But then the real reason this approach is bad for quick testing:

error: could not compile `project` (bin "project" test) due to 1 previous error; 1 warning emitted

Meaning, that if there's an issue somewhere else in my project (not in my_module), tests don't work at all! This wouldn't be an issue using the Rails console - only the code that's run needs to work.

Alternatively, appending the statement to the beginning of main() and running the entire project (thus avoiding having to write the test boilerplate) only works for some projects, and it still doesn't work if the project doesn't compile.

Rust has no production-ready (or even usable) REPL to my knowledge. For compiled languages, that's a hard problem, actually.

WDYM by "a test I'll only use once"? Why can't you keep that test?

By the way, you are not actually testing anything in that "test". You should incorporate the expected value into it and turn the print into an assert_eq!() or similar – currently, even if your function doesn't produce the expected value, the "test" will still run without panicking and pass.

In a way, Rust's approach is superior, because it encourages you to write actual tests that you can rely on later for refactoring. You don't get that by running one statement in a REPL once.

2 Likes

Thanks for your response :grinning:

By the way, you are not actually testing anything in that "test"

I don't want to keep that test exactly for this reason. It's just a quick sanity test. If I wanted an actual test, I would have written it like you suggested.

And I don't think Rust approach is necessarily superior. Ruby on Rails allows quick sanity tests using the console and Rust-like serious tests using RSpec. I agree Rust is encouraging me to write serious tests by not having this feature, but I don't know if that's a good thing xD

Rust has no REPL and what I usually do when I don't want to write a test (maybe because the function I am writing is just an experiment and I don't know exactly where I am going) is to have a separate binary I call "sandbox" (in bin/sandbox.rs) that contains just a main function with the code that calls into my library and I run it with cargo run --bin sandbox.

But I have to admit that the more I get proficient with Rust the more I don't need such a hack: it feels much better to write the function, the corresponding tests and then just run cargo test and profit. Also, I don't see a reason in writing code that I'll later delete: I just keep all the tests, even the obvious ones: they are good starting points for later expansions.

3 Likes

That makes sense :slight_smile: Thanks for your suggestions!

There's a cool tool I use sometimes called evcxr. evcxr/evcxr_repl/README.md at main · evcxr/evcxr · GitHub

I use it like a rust repl and have rust aliased in my shell to it because I can never remember if it's evcxr or evxcr.

I think you might be able accomplish what you're wanting with this tool.

3 Likes

Better check whether you turned lto on. Turn off it may speed up the test by a little.

More, if you turn off lto, setting your dependency lib as dylib may help.

Haha, same :joy:.
I don't use it much though. It always seems like more work to set up everything I need in a REPL that will be deleted again after closing it than just writing a test or a throw-away binary as @fogzot suggested.

To get rid of compiling errors when running tests. replace the errors with todo!() or unimplemented!() could be instant help.(at least for me).

Also, TDD might be a good approach, so, maybe you can try not abadoning your tests.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.