How to import [op] function in mod tests? - unit-testing

I am testing a function with an attribute #[op] (deno_ops proc_macro op):
#[op]
#[instrument(name = "op_xyz", skip_all, ret)]
async fn xyz() -> Result<U256, deno_core::error::AnyError> {...}
I have written the tests like this:
#[cfg(test)]
mod tests {
use super::op_xyz;
#[tokio::test]
async fn test_op_get_latest_nonce() {
let res = op_xyz().await;
...
}
}
But I'm getting the error:
expected function, found `op_xyz`
Below is what the VS Code Rust plugin says about the error:
Auto-generated by deno_ops, i.e: #[op]
Use op_get_latest_nonce::decl() to get an op-declaration you can include in a deno_core::Extension
Note: The function is working perfectly & I can test it outside the mod tests, so the issue seems with importing op_xyz inside it due to its #[op] attribute.
Usage outside mod tests
#[op]
#[instrument(name = "op_xyz", skip_all, ret)]
async fn xyz() -> Result<U256, deno_core::error::AnyError> {...}
#[tokio::test]
async fn test_op_xyz() {
let res = op_xyz().await;
...
}
Then running cargo test test_op_xyz -- --nocapture returns the correct value.

Related

Kotlin & MockK - mocking not working if a mocked method is called from another method

I have a problem with MockK.
I have a class:
#Service
class ItemServiceImpl(private val varPuObjectMapper: VarPuObjectMapper) : OutboundAdvicesService {
override suspend fun getItemsForWarehouse(warehouseId: String): ItemsDTO {
// do stuff
}
override suspend fun getPickingListsForWarehouse(warehouseId: String): PickingListsDTO {
val groupedOutboundAdvices = getItemsForWarehouse(warehouseId)
// do other stuff
}
}
and a test for this class:
class ItemServiceGroupingTest : FunSpec({
val warehouseId = "1"
val myObjectMapper = MyObjectMapper()
val itemService = mockk<ItemServiceImpl>()
beforeTest {
val items1 = myObjectMapper
.getObjectMapper()
.readValue(Mockups.ITEMS_1, ItemsDTO::class.java)
coEvery {
itemService.getItemsForWarehouse(warehouseId)
} returns items1
}
test("should get items for warehouse with ID 1") {
val itemsDTO = itemService.getItemsForWarehouse(warehouseId)
// assertions here
}
test("should get picking lists for warehouse with ID 1") {
val pickingLists = itemService.getPickingListsForWarehouse(warehouseId)
// assertions here
}
})
Now the first test passes successfully, but the second one fails:
no answer found for: ItemServiceImpl(#1).getPickingListsForWarehouse(1, continuation {})
io.mockk.MockKException: no answer found for: ItemServiceImpl(#1).getPickingListsForWarehouse(1, continuation {})
at app//io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:93)
From what I understand, this fails cause the getPickingListsForWarehouse method is not mocked. Is it possible to call a real method using MockK? I tried to use spyk instead of mockk, and I tried mockk with relaxed = true, but it got me nowhere...
The problem with the second test is that you are trying to call a method from a mock without specified behavior. The first test passes because you already set the value which should be returned for the method call itemService.getItemsForWarehouse(warehouseId) in this statement in beforeTest:
coEvery {
itemService.getItemsForWarehouse(warehouseId)
} returns items1
You have to do the same for getPickingListsForWarehouse or call a real method like:
every { itemService.getPickingListsForWarehouse(warehouseId) } answers { callOriginal() }
But then you have to use spyk instead of mock.
However, if you are asserting the object which you provided within the mock, you are not testing the real implementation of the method under test. You are just testing the mock, so if you change the implementation of your method this test still will be passing. beacuse it doesn't call your real object.

How can I set a caller in ink! contract unit testing function?

fn do_check(&mut self) -> Result<()> {
let caller = self.env().caller();
...
}
I am writing a test function for do_check function. Here, I want to set a caller but not sure how to do that.
#[cfg(test)]
mod tests {
use super::*;
use ink_lang as ink;
#[ink::test]
fn do_check_works() {
let mut test = Test::new();
// here I want to set a caller for calling do_check
test.do_check();
...
You can set the caller using set_caller from ink_env:
let account = AccountId::from([0x1; 32]);
ink_env::test::set_caller::<ink_env::DefaultEnvironment>(account);
EDIT: Currently, you need the experimental unit test engine. Add this above your test mod:
#[cfg(feature = "ink-experimental-engine")]
And add the dependency in your toml file:
ink-experimental-engine = ["ink_env/ink-experimental-engine"]
See the examples in the ink repo for more details.

How to run setup code before any tests run in Rust?

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.

How to give a mocked value to a function which needs a reference with the 'static lifetime and implements Sync?

I used mocker to test my project. I have no idea what kind of type conversion is needed by say_hello_brother. The simplified code list below:
lib.rs
#![feature(plugin, custom_derive)]
#![plugin(mockers_macros)]
#[cfg(test)]
extern crate mockers;
use mockers::Scenario;
#[derive(Mock)]
trait SayHello {
fn hello(&self);
}
// assume `SayHello` is a service and worked on multiple threads
fn say_hello_brother<T: SayHello + Sync>(brother: &'static T) {
brother.hello()
}
#[test]
fn test_sya_hello() {
let scenario = Scenario::new();
let mock = scenario.create_mock_for::<SayHello>();
say_hello_brother(&mock)
}
Cargo.toml
[package]
name = "mock"
version = "0.1.0"
authors = ["llxxb"]
[dependencies]
mockers = "0.9.4"
mockers_macros = "0.9.4"
and error info:
error[E0277]: the trait bound `std::rc::Rc<std::cell::RefCell<mockers::ScenarioInternals>>: std::marker::Sync` is not satisfied in `SayHelloMock`
--> src\lib.rs:22:5
|
22 | say_hello_brother(&mock)
| ^^^^^^^^^^^^^^^^^ `std::rc::Rc<std::cell::RefCell<mockers::ScenarioInternals>>` cannot be shared between threads safely
|
= help: within `SayHelloMock`, the trait `std::marker::Sync` is not implemented for `std::rc::Rc<std::cell::RefCell<mockers::ScenarioInternals>>`
= note: required because it appears within the type `SayHelloMock`
UPDATE After some trying, I succeed to attach Sync to SayHelloMock. the new lib.rs:
#![feature(plugin, custom_derive)]
#![plugin(mockers_macros)]
#[cfg(test)]
extern crate mockers;
use mockers::Scenario;
trait SayHello {
fn hello(&self);
}
mock! {
SayHelloMock,
self,
trait SayHello {
fn hello(&self);
}
}
unsafe impl Sync for SayHelloMock {}
// assume `SayHello` is a service and worked on multiple threads
fn say_hello_brother<T: SayHello + Sync>(brother: &'static T) {
brother.hello()
}
#[test]
fn test_sya_hello() {
let scenario = Scenario::new();
// not work
// let mock = scenario.create_mock::<SayHelloMock>();
// static MOCK: SayHelloMock = || { mock };
// say_hello_brother(&MOCK)
// not work yet
let mock : &'static SayHelloMock = &(scenario.create_mock::<SayHelloMock>());
say_hello_brother(mock)
}
But I still can't convert it to 'static, error info:
error[E0597]: borrowed value does not live long enough
--> src\lib.rs:38:41
|
38 | let mock : &'static SayHelloMock = &(scenario.create_mock::<SayHelloMock>());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
39 | say_hello_brother(mock)
40 | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
You cannot use the mocker library for this purpose because the mocks it generates are not thread safe.
That being said, nothing restricts you from creating your own mocks which are thread-safe:
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug, Default)]
struct MyMock {
call_count: AtomicUsize,
}
impl SayHello for MyMock {
fn hello(&self) {
self.call_count.fetch_add(1, Ordering::SeqCst);
}
}
However, your signature requires a &'static T, which is really probably a terrible idea. You will need to use something like a lazy-static:
#[macro_use]
extern crate lazy_static;
#[test]
fn test_say_hello() {
lazy_static! {
static ref MOCK: MyMock = MyMock::default();
};
say_hello_brother(&*MOCK);
assert_eq!(MOCK.call_count.load(Ordering::SeqCst), 1);
}
The question is a little vague, but if I understood correctly, you want to know how you could pass that mock to function say_hello_brother. The problem is that that function expects an object that implements the Sync trait as well. For this reason, you cannot convert mock to some other type to get the code to compile.
According to the documentation of mocker, you could try mocking two traits at once. Here is some pseudo-code that illustrates the idea:
mock! {
SayHelloMock,
self,
trait SayHello {
// trait methods here
},
self,
trait Sync {
// trait methods here
}
}
Then in your test, you would create a mock like this:
let mut mock = scenario.create_mock::<SayHelloMock>();

How to make a variable with a scope/lifecycle for all test functions in a Rust test?

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
}