Skip to content
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

Enhancement: Mocking extension members #263

Closed
ResoDev opened this issue Jun 26, 2020 · 8 comments
Closed

Enhancement: Mocking extension members #263

ResoDev opened this issue Jun 26, 2020 · 8 comments

Comments

@ResoDev
Copy link

ResoDev commented Jun 26, 2020

I am fully aware of the current limitations of this otherwise amazing library. For example, we cannot mock static or top-level methods. Since extensions are really just static members in disguise, Mockito doesn't support them as well.

Extension methods can simplify code a lot and I'd like to use them. However, the inability to use them with mocks forces me to create ugly wrapper classes.

The following code outputs ERROR: Bad state: Mock method was not called within when(). Was a real method called? and also runs the actual code inside the userRef() extension method.

when(mockFirebaseStorage.userRef())
    .thenAnswer((_) async => mockRootStorageReference);

Mockito shouldn't prevent us from using newer language features.

@srawlins
Copy link
Member

Mockito shouldn't prevent us from using newer language features.

How does mockito prevent one from using newer language features? Can you give an example? (In your code snippet, what are mockFirebaseStorage and userRef()?)

@ResoDev
Copy link
Author

ResoDev commented Jun 30, 2020

I mean that we can't mock extension methods.

mockFirebaseStorage is simply an instance of

class MockFirebaseStorage extends Mock implements FirebaseStorage {}

and userRef() is an extension method

extension FirebaseStorageX on FirebaseStorage {
  Future<StorageReference> userRef() async {
    // ...
  }
}

@natebosch
Copy link
Member

Mockito does not prevent you from using extension methods, you can mock out the behavior used by the extension instead of the entire extension. It also does not allow you to mock extension methods, because the language fundamentally does not support that.

Just as with any other static methods which can't be mocked - it is your choice to use them or avoid them. Personally I don't avoid these language features and if I find myself missing the ability to mock out some bit of static functionality I consider it a design smell (overly tight coupling) or testing smell (over reliance on mocking).

This issue is not actionable in this package.

@LHDi
Copy link

LHDi commented Jul 13, 2020

Is there a known method to do so ( Mock the behavior of extension ) because I couldn't get it to work.
And if it's possible to add the method to the documentation.

@srawlins
Copy link
Member

There is no method to mock extension methods. This is already documented.

@LHDi
Copy link

LHDi commented Jul 13, 2020

I meant the behavior used by the extension as @natebosch mentioned:

... you can mock out the behavior used by the extension instead of the entire extension.

and can I apply it to an external package such as permission_handler?
They used extension methods to implement the package, So is it unMockable

Thank you.

@natebosch
Copy link
Member

There is no magic here - mocking the behavior that is used is what you already do with mocks. You are accustomed to mocking the behavior that is used by your code under test - that is the purpose of the mock. When I say to mock the behavior used by the extension, I mean to treat the extension that your code under test calls like any other code in the implementation.

To make this explicit consider this trivial example:

abstract class Foo {
  int get a;
  int get b;
}

extension AandB on Foo {
  int get aAndB => a + b;
}

You know that your code under test calls someFoo.aAndB, and so you wish that you could do when(mockFoo.aAndB).thenReturn(10);, but since aAndB is an extension member you can't do that. Instead, you need to do:

when(mockFoo.a).thenReturn(4);
when(mockFoo.b).thenReturn(6);

And when your code under test reads aAndB it will get the intended value 10. What this is doing is forcing the test to match the actual requirements/behavior of the code under test - which is that the 2 fields a and b sum to 10.

@LHDi
Copy link

LHDi commented Jul 14, 2020

Thank you @natebosch I found your answer very helpful.
I think it's now clear that this is not a Mockito issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants