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

Allow mocking of static methods #97

Open
moltmann opened this issue Apr 6, 2018 · 1 comment
Open

Allow mocking of static methods #97

moltmann opened this issue Apr 6, 2018 · 1 comment

Comments

@moltmann
Copy link
Contributor

moltmann commented Apr 6, 2018

@mockitoguy, @drewhannay

The Android API uses static methods, e.g. in Settings.Global . Some tests might want to mock those.

In mockito/mockito#1013 there is a discussion for adding that to mockito. Unfortunately it might take some time to get this into mockito as we need

  • a (non-Android) JVM implementation
  • Agree on and API and get it approved

Hence I am proposing to prototype this as a variant of dexmaker-mockito-inline. Once this has proved useful we can upstream it to mockito.

I propose the following agena:

  1. Add a new sub-project dexmaker-mockito-inline-extended. Prototype
  2. Wrap the Mockito class and add static mocking. Proposal by @mockitoguy, Full java-doc of Proposal
  3. The new APIs need to be clearly marked as "unstable"
  4. Ship this extended mockito and get feedback
  5. Work with mockito to upstream the new APIs
moltmann added a commit to moltmann/dexmaker that referenced this issue Apr 7, 2018
Similar to stubbing of instance methods this uses an JVMTI agent to add a
method entry hook to all affected methods. This change adds a new JVMTI
agent that is only used for stubbing of static methods. This comes with
its own mockMaker that hooks into the multiplexer added in a previous
change.

Most aspects of stubbing static methods are quite similar to instance
methods. Here are the differences:

== Scope of mocking ==

For instance methods the methods stay stubbed until the object goes
away. For static methods there is no object. Hence from the user point
of view the scope is defined by a MockitoSession. Internally we use a
marker object to track all stubbing of static methods for a class.

We use ThreadLocal variables to communicate between the frontEnd i.e.
ExtendedMockito and the backend i.e. InlineStaticMockMaker.

== Highlander rule for static mocking ===

As we set up mocking/spying per class, we can only mock/spy a class once
per session.

== 'inherited' static methods ==

In the following case:

---
class SuperClass {
    static String returnA() { return "A"; }
}

class SubClass {
}

@test
public void test() {
    SubClass.returnA();
}
---

The backtraces actually contains SuperClass.returnA(). We have to look
at the byte code of the calling method to figure out if the method was
called on the super or subclass. While this is _super_expensive_ (~10ms
in my unscientific experiments) I see no other way.
@moltmann
Copy link
Contributor Author

moltmann commented Apr 7, 2018

Pull 98 covers step 1 - 3 of above agenda

drewhannay pushed a commit that referenced this issue Apr 21, 2018
Similar to stubbing of instance methods this uses an JVMTI agent to add a
method entry hook to all affected methods. This change adds a new JVMTI
agent that is only used for stubbing of static methods. This comes with
its own mockMaker that hooks into the multiplexer added in a previous
change.

Most aspects of stubbing static methods are quite similar to instance
methods. Here are the differences:

== Scope of mocking ==

For instance methods the methods stay stubbed until the object goes
away. For static methods there is no object. Hence from the user point
of view the scope is defined by a MockitoSession. Internally we use a
marker object to track all stubbing of static methods for a class.

We use ThreadLocal variables to communicate between the frontEnd i.e.
ExtendedMockito and the backend i.e. InlineStaticMockMaker.

== Highlander rule for static mocking ===

As we set up mocking/spying per class, we can only mock/spy a class once
per session.

== 'inherited' static methods ==

In the following case:

---
class SuperClass {
    static String returnA() { return "A"; }
}

class SubClass {
}

@test
public void test() {
    SubClass.returnA();
}
---

The backtraces actually contains SuperClass.returnA(). We have to look
at the byte code of the calling method to figure out if the method was
called on the super or subclass. While this is _super_expensive_ (~10ms
in my unscientific experiments) I see no other way.
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

1 participant