-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API change ideas for elegant mocking #33
Comments
An idea for the struct instance mocking: #![mockable]
struct Struct(u8);
impl Struct {
fn get(&self) -> u8 {
self.0
}
} via macro magic complied to this: struct StructOriginal(u8);
impl StructOriginal {
fn get(&self) -> u8 {
self.0
}
}
struct Struct {
id: ::uuid::Uuid,
original: Struct,
}
impl Struct {
fn get(&self) -> u8 {
// your function header injection here where you could test on instance equality via
// self.id, then remove &self from params list on let `called_with` handle the rest
self.original.get()
}
} The problem would be the direct access to properties (pattern matching, constructing via something like Needed to write this down this concise, so I don't forget it :D I'm just really excited on having a full-fledged mocking library for rust! |
Oh, I have a better idea than my last comment. The following works with your latest release: pub(crate) struct Struct(u8);
#[mockable]
impl Struct {
pub(crate) fn get(&self) -> u8 {
self.0
}
}
#[test]
fn test_instance_mock() {
let a = Struct(1);
let b = Struct(2);
println!("address a: {:?}", &a as *const _);
println!("address b: {:?}", &b as *const _);
unsafe {
Struct::get.mock_raw(|instance| {
println!("address in call: {:?}", instance as *const _);
if instance as *const _ == &a as *const _ {
MockResult::Return(42)
} else {
MockResult::Continue((instance,))
}
});
}
assert_eq!(a.get(), 42);
assert_eq!(b.get(), 2);
} which results in following output:
It is actually possible (based on my little research and testing) with your implementation as base to build what I proposed. |
I'm very sorry, but since a few days and for a few more I will not be able to work on Mocktopus. I will gladly come back to coding, reviewing and discussing when my personal situation gets a bit more stable and less time consuming. |
I agree with @Gerschtli . Mocktopus needs an expectation API before it can be considered a full-featured mocking library. It should be able to expect that a function is called with certain arguments a certain number of times. It should also be able to expect that different methods, or different invocations of the same method, are called in a certain order. |
This is matter of taste, but personally I'm not a fan of verbose API, you've proposed in the first comment. I've been using similar tools in Java and C# and it was very frustrating, I was plowing through docs for hours to understand or compose even relatively simple mocks. The proposed API needs some refinement and probably will get much more complex:
Unfortunately the first proposition of per-object mocking seems completely unrealistic, I have no idea how could we tackle the technical difficulties. The second one makes a lot of sense, but it requires immovability of the object. If we bake pointer comparison into an API, it probably should require object to be I may seem negative, but I've intentionally avoided this route of Mocktopus API. I've decided that a trivial API and usage of regular Rust logic is better than huge toolkit of complex, use-case-specific functions. Less maintenance, lower learning curve and unlimited elasticity. BUT I'm planning to switch trait Mock<T, O> {
fn call_mock(&mut self, args: T) -> MockResult<T, O>;
} That means 2 things:
|
I understand what you mean but in contrast to your point of view I think an easily readable test should be a key feature of a mocking/testing framework. Tests are meant to be living documentation, to document the expected behaviour and ensure functionality. The main goal is for me a more readable and understandable test, for point 1: I am currently experimenting with python like method decorators to build fixtures. This could be easily be implemented as such. Needs more testing of course. for point 2 and 3: it could be handled with a macro. How to build it to be the most intuitive is another discussion. for point 4: Why not use the matcher of https://github.com/Valloric/hamcrest2-rust ? for point 5: We could also accept The per object mocking is apart from the technical difficulties a very interesting feature. Even if we include the custom comparison to test for the correct instance. Of course if you don't want to build such a framework, it would be totally fine for me to create a library around mocktopus. |
Hey,
what do you think of adding a completly new API to improve readability of mockings. I would suggest something like this:
A call to
expected
would be optional and defaulting totimes(1)
. Other parameter values could beat(2)
,al_least(3)
etc.called_with
would be a method accepting a hamcrest2 matcher for each parameter. Internal we could simply test these withassert_that!(expected_value, <matcher>)
. This method call would also be optional, defaulting to empty parameter list.returns
will simply specify, what the function returns. Alternativelyruns_original()
will delegate the call to the original implementation.The only thing what I need to call this a full mocking library would be the ability to mock the behavior for struct instances, like:
It would be a nice feature to be able to configure (via parameter, function call, etc) if calls not specified in
expected
raise an error and let the test fail, like:For struct instances, you could specify with anything like the following, where the test would fail on any call to a method of that struct instance.
These are just some ideas inspired by Mockito, PHPUnit and similar mocking libraries. What do you think of it? Would it be something you want to add in this crate?
I would love to help and contribute to your project if so.
The text was updated successfully, but these errors were encountered: