I have a Rust app (a simple interpreter) that needs some setup (initialize a repo) before the environment is usable.
I understand that Rust runs its tests (via cargo test) in a multithreaded manner, so I need to initialize the repo before any tests run. I also need to do this only once per run, not before each test.
In Java's JUnit this would be done with a #BeforeClass (or #BeforeAll in JUnit 5) method. How can I acheive the same thing in Rust?
There's nothing built-in that would do this but this should help (you will need to call initialize() in the beginning of every test):
use std::sync::Once;
static INIT: Once = Once::new();
pub fn initialize() {
INIT.call_once(|| {
// initialization code here
});
}
If you use the ctor crate, you can take advantage of a global constructor function that will run before any of your tests are run.
Here's an example initialising the popular env_logger crate (assuming you have added ctor to your [dev-dependencies] section in your Cargo.toml file):
#[cfg(test)]
#[ctor::ctor]
fn init() {
env_logger::init();
}
The function name is unimportant and you may name it anything.
Just to give people more ideas (for example, how not to call setup in every test), one additional thing you could do is to write a helper like this:
fn run_test<T>(test: T) -> ()
where T: FnOnce() -> () + panic::UnwindSafe
{
setup();
let result = panic::catch_unwind(|| {
test()
});
teardown();
assert!(result.is_ok())
}
Then, in your own tests you would use it like this:
#[test]
fn test() {
run_test(|| {
let ret_value = function_under_test();
assert!(ret_value);
})
}
You can read more about UnwindSafe trait and catch_unwind here: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
I've found the original idea of this test helper in this medium article by Eric Opines.
Also, there is rstest crate which has pytest-like fixtures which you can use as a setup code (combined with the Jussi Kukkonen's answer:
use std::sync::Once;
use rstest::rstest;
static INIT: Once = Once::new();
pub fn setup() -> () {
INIT.call_once(|| {
// initialization code here
});
}
#[rstest]
fn should_success(setup: ()) {
// do your test
}
Maybe one day rstest will gain scopes support and Once won't be needed anymore.
Related
I'm trying to test a struct I have that looks something like this
struct CANProxy {
socket: CANSocket
// other stuff .......
}
impl CANProxy {
pub fn new(can_device: &str) -> Self {
let socket = CANSocket::open(can_device).unwrap();
// other stuff .......
Self { socket }
}
}
What I want to test is that the proper messages are being sent across the socket, but I don't want to actually initialize a new can device while running my tests. I wanted to make a dummy CANSocket (which is from the cansocket crate) that uses the same functions and whatnot.
I tried creating a trait and extending the socketcan::CANSocket but it is super tedious and very redundant. I've looked at the mockall crate but I'm not sure if this would help in this situation. Is there an elegant way to accomplish what I want?
trait CANInterface {
fn open(name: &str) -> Result<Self, SomeError>;
// ... all the functions that are a part of the socketcan::CANSocket
// which is a lot of repetition
}
///////////// Proxy code
struct<T: CANInterface> CANProxy<T> {
socket: T
// other stuff .......
}
impl<T: CANInterface> CANProxy<T> {
pub fn open(can_device: &str) -> Result<Self, SomeError> {
let socket = T::open(can_device).unwrap();
// other stuff .......
Ok(Self { socket })
}
}
////////////// Stubbed CANInterfaces
struct FakeCANSocket;
impl CANInterface for FakeCANSocket {
// ..... implementing the trait here
}
// extension trait over here
impl CANInterface for socketcan::CANSocket {
// this is a lot of repetition and is kind of silly
// because I'm just calling the same things
fn open(can_device: &str) -> Self {
CANSocket::open(can_device)
}
/// ..............
/// ..............
/// ..............
}
So, first of all, there are indeed mock-targeted helper tools and crates such as ::mockall to help with these patterns, but only when you already have a trait-based API. If you don't, that part can be quite tedious.
For what is worth, know that there are also other helper crates to help write that boiler-plate-y and redundantly-delegating trait impls such as your open -> open situation. One such example could be the ::delegate crate.
Mocking it with a test-target Cargo feature
With all that being said, my personal take for your very specific situation —the objective is to override a genuine impl with a mock one, but just for testing purposes—, would be to forgo the structured but heavyweight approach of generics & traits, and to instead embrace "duck-typed" APIs, much like it is often done when having implementations on different platforms. In other words, the following suggestion, conceptually, could be interpreted as your test environment being one such special "platform".
You'd then #[cfg(…)]-feature-gate the usage of the real impl, that is, the CANSocket type, in one case, and #[cfg(not(…))]-feature gate a mock definition of your own CANSocket type, provided you managed to copy / mock all of the genuine's type API that you may, yourself, be using.
Add a mock-socket Cargo feature to your project:
[features]
mock-socket = []
Remark: some of you may be thinking of using cfg(test) rather than cfg(feature = "…"), but that approach only works for unit (src/… files with #[cfg(test)] mod tests, cargo test --lib invocation) tests, it doesn't for integration tests (tests/….rs files, cargo test --tests invocation) or doctests (cargo test --doc invocation), since the library itself is then compiled without cfg(test).
Then you can feature-gate Rust code using it
#[cfg(not(feature = "mock-socket"))]
use …path::to::genuine::CANSocket;
#[cfg(feature("mock-socket"))]
use my_own_mock_socket::CANSocket;
So that you can then define that my_own_mock_socket module (e.g., in a my_own_mock_socket.rs file using mod my_own_mock_socket; declaration), provided you don't forget to feature-gate it itself, so that the compiler doesn't waste time and effort compiling it when not using the mocked CANSocket (which would yield dead_code warnings and so on):
#[cfg(feature = "mock-socket")]
mod my_own_mock_socket {
//! It is important that you mimic the names and APIs of the genuine type!
pub struct CANSocket…
impl CANSocket { // <- no traits!
pub fn open(can_device: &str) -> Result<Self, SomeError> {
/* your mock logic */
}
…
}
}
That way, you can use:
either cargo test
or cargo test --features mock-socket
to run pick the implementation of your choice when running your tests
(Optional) if you know you will never want to run the tests for the real implementation, and only the mock one, then you may want to have that feature be enabled by default when running tests. While there is no direct way to achieve this, there is a creative way to work around it, by explicitly telling of the self-as-a-lib dev-dependency that test code has (this dependency is always present implicitly, for what is worth). By making it explicit, we can then use the classic features .toml attribute to enable features for that dev-dependency:
[dev-dependencies]
your_crate_name = { path = ".", features = ["mock-socket"] }
Bonus: not having to define an extra module for the mock code.
When the mock impls in question are short enough, it could be more tempting to just inline its definition and impl blocks. The issue then is that for every item so defined, it has to carry that #[cfg…] attribute which is annoying. That's when helper macros such as that of https://docs.rs/cfg-if can be useful, albeit adding a dependency for such a simple macro may seem a bit overkill (and, very personally, I find cfg_if!'s syntax too sigil heavy).
You can, instead, reimplement it yourself in less than a dozen lines of code:
macro_rules! cfg_match {
( _ => { $($tt:tt)* } $(,)? ) => ( $($tt)* );
( $cfg:meta => $expansion:tt $(, $($($rest:tt)+)?)? ) => (
#[cfg($cfg)]
cfg_match! { _ => $expansion }
$($(
#[cfg(not($cfg))]
cfg_match! { $($rest)+ }
)?)?
);
} use cfg_match;
With it, you can rewrite steps 2. and 3. above as:
cfg_match! {
feature = "mock-socket" => {
/// Mock implementation
struct CANSocket …
impl CANSocket { // <- no traits!
pub fn open(can_device: &str) -> Result<Self, SomeError> {
/* your mock logic */
}
…
}
},
_ => {
use …path::to::genuine::CANSocket;
},
}
You can avoid a lot of the boilerplate by using a macro to create the wrapper trait and implement it for the base struct. Simplified example:
macro_rules! make_wrapper {
($s:ty : $t:ident { $(fn $f:ident ($($p:ident $(: $pt:ty)?),*) -> $r:ty;)* }) => {
trait $t {
$(fn $f ($($p $(: $pt)?),*) -> $r;)*
}
impl $t for $s {
$(fn $f ($($p $(: $pt)?),*) -> $r { <$s>::$f ($($p),*) })*
}
}
}
struct TestStruct {}
impl TestStruct {
fn foo (self) {}
}
make_wrapper!{
TestStruct: TestTrait {
fn foo (self) -> ();
}
}
Playground
This will need to be extended to handle references (at least &self arguments), but you get the idea. You can refer to The Little Book of Rust Macros for more information on writing the macro.
Then you can use a crate like mockall to create your mock implementation of TestTrait or roll your own.
I’m working on a Rust library that provides access to some hardware devices. There are two device types, 1 and 2, and the functionality for type 2 is a superset of the functionality for type 1.
I want to provide different test suites for different circumstances:
tests with no connected device (basic sanity checks, e. g. for CI servers)
tests for the shared functionality (requires a device of type 1 or 2)
tests for the type 2 exclusive functionality (requires a device of type 2)
I’m using features to represent this behavior: a default feature test-no-device and optional features test-type-one and test-type-two. Then I use the cfg_attr attribute to ignore the tests based on the selected features:
#[test]
#[cfg_attr(not(feature = "test-type-two"), ignore)]
fn test_exclusive() {
// ...
}
#[test]
#[cfg_attr(not(any(feature = "test-type-two", feature = "test-type-one")), ignore)]
fn test_shared() {
// ...
}
This is rather cumbersome as I have to duplicate this condition for every test and the conditions are hard to read and maintain.
Is there any simpler way to manage the test suites?
I tried to set the ignore attribute when declaring the module, but apparently it can only be set for each test function. I think I could disable compilation of the excluded tests by using cfg on the module, but as the tests should always compile, I would like to avoid that.
Is there a simple way to conditionally enable or ignore entire test suites in Rust?
The easiest is to not even compile the tests:
#[cfg(test)]
mod test {
#[test]
fn no_device_needed() {}
#[cfg(feature = "test1")]
mod test1 {
fn device_one_needed() {}
}
#[cfg(feature = "test2")]
mod test2 {
fn device_two_needed() {}
}
}
I have to duplicate this condition for every test and the conditions are hard to read and maintain.
Can you represent the desired functionality in pure Rust? yes
Is the existing syntax overly verbose? yes
This is a candidate for a macro.
macro_rules! device_test {
(no-device, $name:ident, {$($body:tt)+}) => (
#[test]
fn $name() {
$($body)+
}
);
(device1, $name:ident, {$($body:tt)+}) => (
#[test]
#[cfg_attr(not(feature = "test-type-one"), ignore)]
fn $name() {
$($body)+
}
);
(device2, $name:ident, {$($body:tt)+}) => (
#[test]
#[cfg_attr(not(feature = "test-type-two"), ignore)]
fn $name() {
$($body)+
}
);
}
device_test!(no-device, one, {
assert_eq!(2, 1+1)
});
device_test!(device1, two, {
assert_eq!(3, 1+1)
});
the functionality for type 2 is a superset of the functionality for type 1
Reflect that in your feature definitions to simplify the code:
[features]
test1 = []
test2 = ["test1"]
If you do this, you shouldn't need to have any or all in your config attributes.
a default feature test-no-device
This doesn't seem useful; instead use normal tests guarded by the normal test config:
#[cfg(test)]
mod test {
#[test]
fn no_device_needed() {}
}
If you follow this, you can remove this case from the macro.
I think if you follow both suggestions, you don't even need the macro.
I have a test that initializes a variable before diving into the detail of the test, and I want to make a second test with the same variable, and not duplicate the initialization code:
#[test]
fn test_one() {
let root = Path::new("data/");
// the rest of the test
}
#[test]
fn test_two() {
let root = Path::new("data/");
// the rest of the test
}
I don't think static or const would do it because the size would not be known up front, though PathBuf.from(path) might make that OK, except that initialization expressions for static/const vars cannot be too complex.
I've seen lazy_static, but have not seen any examples of its use in tests. This after seeing the compiler error with "an extern crate loading macros must be at the crate root", which online searching tells me is something about being outside main(), but tests don't have main functions.
In Java, I would define the variable then initialize it in a setup() method, but I can't see examples of that online for Rust.
Foremost, remember that Rust tests are run in parallel. This means that any shared setup needs to be thread-safe.
and not duplicate the initialization code
You do it the same way you avoid duplicating any other code: create a function, create a type, create traits, etc.:
use std::path::PathBuf;
fn root() -> PathBuf {
PathBuf::from("data/")
}
#[test]
fn test_one() {
let root = root();
// the rest of the test
}
#[test]
fn test_two() {
let root = root();
// the rest of the test
}
In Java I would define the variable, then initialize it in a setup() method
Instead, make a struct called Setup containing all those variables and construct it as the first thing in each test:
use std::path::{Path, PathBuf};
struct Setup {
root: PathBuf,
}
impl Setup {
fn new() -> Self {
Self {
root: PathBuf::from("data/"),
}
}
}
#[test]
fn test_one() {
let setup = Setup::new();
let root: &Path = &setup.root;
// the rest of the test
}
#[test]
fn test_two() {
let setup = Setup::new();
let root: &Path = &setup.root;
// the rest of the test
}
but have not seen any examples of [lazy-static] use in tests
That's because there is no different way to use it in tests, it's just code:
use lazy_static::lazy_static; // 1.4.0
use std::path::Path;
lazy_static! {
static ref ROOT: &'static Path = Path::new("data/");
}
#[test]
fn test_one() {
let root = *ROOT;
// the rest of the test
}
#[test]
fn test_two() {
let root = *ROOT;
// the rest of the test
}
See also:
How to initialize the logger for integration tests?
How do I create a global, mutable singleton?
Very specifically for your case, it's very rare that you need exactly a Path, since a string slice implements AsRef<Path>. Said another way, most places that accept a Path accept a &str:
static ROOT: &str = "data/";
#[test]
fn test_one() {
let root = ROOT;
// the rest of the test
}
#[test]
fn test_two() {
let root = ROOT;
// the rest of the test
}
It is important to me to be able to assert how many times a fake / mocked method is called in my tests and I'm wondering what is the best way to do this without using something like testify. In my case, the call to the mocked method is the result of some recursive call.
Lets say I have table driven tests with various animals, I want to assert that Hello is actually called for some tests but not for others. In some cases, it should be called more than once for given test (iterating over a slice).
Is it appropriate to just add a counter and make an assertion on that in my table driven test? It seems to me like maybe there is a better way to do this.
If I do add a counter to the hello method... where is it appropriate to deal with and check this. In the fake method itself or in the test etc?
type fakeFarmService struct {
abc.someFarmServiceInterface
}
func (f *fakeFarmService) Hello(ctx context.Context, in *abc.FarmRequest) (*abc.FarmResponse, error) {
if in.GetAnimal() == Monkey {
return &abc.HelloResponse{}, nil
}
return nil, errors.New("an error")
}
I've used the approach of counter on the struct and then asserting it inside the package level unit test multiple times in the past. Still, it's probably only until the level of package, when you would like to test such an internal assertions. I believe it's an accepted way of doing this in Go. Just be careful about properly synchronizing the access to the counter, if you decide to use a global variable or run the tests concurrently.
package main
import (
"fmt"
"sync"
"testing"
)
type fakeable interface {
Hello()
}
type fakeFarmService struct {
mu sync.Mutex
counter int
}
func (f *fakeFarmService) Hello() {
f.mu.Lock()
f.counter++
f.mu.Unlock()
}
func helloCaller(callee fakeable) {
callee.Hello()
}
func TestCallingTheHello(t *testing.T) {
fakeSvc := &fakeFarmService{}
helloCaller(fakeSvc)
helloCaller(fakeSvc)
// we expect that Hello method of fakeable was called 2 times
fakeSvc.mu.Lock()
defer fakeSvc.mu.Unlock()
if c := fakeSvc.counter; c != 2 {
t.Errorf("unexpected call count, want 2, got %d", c)
}
}
func main() {
TestCallingTheHello(&testing.T{})
}
https://play.golang.org/p/RXKuLKIZwc (test error won't work inside the playground)
Some good material on advanced testing in Go
Testing Techniques by Andrew Gerrand
NewStore TechTalk - Advanced Testing with Go by Mitchell Hashimoto
How to mock Kotlin extension function using Mockito or PowerMock in tests? Since they are resolved statically should they be tested as static method calls or as non static?
I think MockK can help you.
It supports mocking extension functions too.
You can use it to mock object-wide extensions:
data class Obj(val value: Int)
class Ext {
fun Obj.extensionFunc() = value + 5
}
with(mockk<Ext>()) {
every {
Obj(5).extensionFunc()
} returns 11
assertEquals(11, Obj(5).extensionFunc())
verify {
Obj(5).extensionFunc()
}
}
If you extension is a module-wide, meaning that it is declared in a file (not inside class), you should mock it in this way:
data class Obj(val value: Int)
// declared in File.kt ("pkg" package)
fun Obj.extensionFunc() = value + 5
mockkStatic("pkg.FileKt")
every {
Obj(5).extensionFunc()
} returns 11
assertEquals(11, Obj(5).extensionFunc())
verify {
Obj(5).extensionFunc()
}
By adding mockkStatic("pkg.FileKt") line with the name of a package and file where extension is declared (pkg.File.kt in the example).
More info can be found here: web site and github
First of all, Mockito knows nothing Kotlin specific language constructs. In the end, Mockito will have a look into the byte code. Mockito is only able to understand what it finds there and what looks like a Java language construct.
Meaning: to be really sure, you might want to use javap to deassemble the compiled classfiles to identify the exact names/signatures of the methods you want to mock.
And obviously: when that method is static, you have to user PowerMock, or JMockit; if not, you should prefer to with Mockito.
From a java point of view, you simply avoid mocking static stuff; but of course, things get really interesting, now that different languages with different ideas/concepts come together.
Instance extension functions can be stubbed and verified like this with the help of mockito-kotlin:
data class Bar(thing: Int)
class Foo {
fun Bar.bla(anotherThing: Int): Int { ... }
}
val bar = Bar(thing = 1)
val foo = mock<Foo>()
with(foo) {
whenever(any<Bar>().bla(any()).doReturn(3)
}
verify(foo).apply {
bar.bla(anotherThing = 2)
}
I use mockk library.
For extension file write java name, like this:
#file:JvmName(name = "ExtensionUtils")
package myproject.extension
...
And for fast codding I created file with different extension mocks:
object FastMock {
fun extension() = mockkStatic("myproject.extension.ExtensionUtils")
fun listExtension() = mockkStatic("myproject.extension.ListExtensionUtils")
}
In test call this:
FastMock.listExtension()
every { itemList.move(from, to) } returns Unit