ChatGPT解决这个技术问题 Extra ChatGPT

Why doesn't Mockito mock static methods?

I read a few threads here about static methods, and I think I understand the problems misuse/excessive use of static methods can cause. But I didn't really get to the bottom of why it is hard to mock static methods.

I know other mocking frameworks, like PowerMock, can do that but why can't Mockito?

I read this article, but the author seems to be religiously against the word static, maybe it's my poor understanding.

An easy explanation/link would be great.

Just a side-note: PowerMock is not a mock object library per-se, it just adds those features (mocking statics and ctors) on top of other libraries. We use PowerMock+Mockito at work they float well with each other.

M
Matthias

I think the reason may be that mock object libraries typically create mocks by dynamically creating classes at runtime (using cglib). This means they either implement an interface at runtime (that's what EasyMock does if I'm not mistaken), or they inherit from the class to mock (that's what Mockito does if I'm not mistaken). Both approaches do not work for static members, since you can't override them using inheritance.

The only way to mock statics is to modify a class' byte code at runtime, which I suppose is a little more involved than inheritance.

That's my guess at it, for what it's worth...


The same is true for mocking constructors by the way. Those, too, cannot be changed via inheritance.
It may also be worth adding that some TDD/TBD proponents perceive the lack of static method and constructor mocking as a good thing. They argue that when you find yourself having to mock static methods or constructors, then this is an indicator for poor class design. For instance, when following a puristic IoC approach in assembling your code modules, you will never even have the need to mock statics or ctors in the first place (unless they are part of some black box component of course). See also giorgiosironi.blogspot.com/2009/11/…
I do think mocking tools should give you what you need without assuming they know what's better for you. For example, if I was using a third party library that utilized a static method call that I needed to mock, it would be nice to be able to do so. The idea that a mock framework won't provide you some capability because it is viewed as bad design is fundamentally flawed.
@Lo-Tan - that's like saying that a language should be capable of everything, not assuming it knows better than you. That's just vanity on your part, because they come off as imposing. The problem here is that the "anti/pro static" battle is not clear, and so are the frameworks. I agree that we should have both. But where the facts are clear, I prefer a framework which imposes those facts. That's one way of learning - tools keeping you on track. So you yourself don't have to. But now every noodle head can impose their so-called "good design". "Fundamentally flawed"...
@nevvermind Eh? A high level language is meant to help you and have necessary abstractions so you can focus on the important pieces. A testing library is a tool - a tool I use to produce better quality, and hopefully better designed, code. What's the point of a testing/mock library when it has limitations that may mean I can't use it when I'm having to integrate someone else's poorly designed code? Doesn't seem well thought out, whereas, good languages have been.
J
Jan

If you need to mock a static method, it is a strong indicator for a bad design. Usually, you mock the dependency of your class-under-test. If your class-under-test refers to a static method - like java.util.Math#sin for example - it means the class-under-test needs exactly this implementation (of accuracy vs. speed for example). If you want to abstract from a concrete sinus implementation you probably need an Interface (you see where this is going to)?


Well, I used static methods to provide high-level abstractions, such as a "static persistence facade". Such a facade keeps client code away from the complexities and low-level details of an ORM API, providing a more consistent and easy to use API, while allowing lots of flexibility.
True, but sometimes you may have no choice if for instance you need to mock a static method which is in some third party class.
True, but sometimes we may be dealing with singletons.
The only think that can't be solved by abstracting is too many levels of abstraction... Adding abstraction layers adds complexity, and is often unnecessary. I think of the examples I've seen of frameworks that try to mock System.currentTimeMillis() by wrapping this simple call into a single class. We end up with a singleton class per method instead of simply having methods - only to facilitate testing. And then when you introduce a 3rd-party dep that calls the static method direct instead of through your singleton wrapper, the tests fail anyway...
Also, with Mockito specifically, mocking is also how you assert that interactions were done. In my use case, I need to mock a static method for that purpose. I don't need to change its implementation/return value (in fact, it's the final thing being called in my test, so I can ignore its return value entirely). But because Mockito chose to make these functions overlap, now I find myself needing to mock a static method.
G
Gerold Broser

Mockito [3.4.0] can mock static methods!

Replace mockito-core dependency with mockito-inline:3.4.0. Class with static method: class Buddy { static String name() { return "John"; } } Use new method Mockito.mockStatic(): @Test void lookMomICanMockStaticMethods() { assertThat(Buddy.name()).isEqualTo("John"); try (MockedStatic theMock = Mockito.mockStatic(Buddy.class)) { theMock.when(Buddy::name).thenReturn("Rafael"); assertThat(Buddy.name()).isEqualTo("Rafael"); } assertThat(Buddy.name()).isEqualTo("John"); } Mockito replaces the static method within the try block only.


For me one Testclass gave some really good insight, how to use the new statickMock-Feature: StaticMockTest.java (it's quite important to use the try-Block). Please see also the Bugfixes in Versions 3.4.2 and 3.4.6 and for completeness the original issue 1013.
@Gerold I'm trying to understand more about unit test. Why do we need to mock if we can just assert the static method result directly? like this : assertThat(Buddy.name()).isEqualTo("John"); Isn't this expression already testing the method ?
@web.learner 1) This is just a sample. 2) You don't mock methods that you would like to test (because that's pointless, since you're just testing a stub then) but methods which are used by the method you'd like to test. 3) With the lines outside the try block you're invoking the real static methods. That's often not what one wants at testing (e.g. because there are external dependencies/resources used in this used method that aren't even available at test time). Remember that unit test should be self-contained. ...cont'd
@web.learner ...cont'd – That's where mocking (i.e. replacing the real method with a generic test double [dummy, fake, stub, mock.]) comes into play where you define what the (then faked/mocked/stubbed) used method should return for a certain use/test case once it is called from the method you'd like to test.
It's not only within a try, I am using mockito-inline 4.2.0 and mocked objects are outside try block, manual close does not help
A
Alex Shesterov

As an addition to the Gerold Broser's answer, here an example of mocking a static method with arguments:

class Buddy {
  static String addHello(String name) {
    return "Hello " + name;
  }
}

...

@Test
void testMockStaticMethods() {
  assertThat(Buddy.addHello("John")).isEqualTo("Hello John");

  try (MockedStatic<Buddy> theMock = Mockito.mockStatic(Buddy.class)) {
    theMock.when(() -> Buddy.addHello("John")).thenReturn("Guten Tag John");
    assertThat(Buddy.addHello("John")).isEqualTo("Guten Tag John");
  }

  assertThat(Buddy.addHello("John")).isEqualTo("Hello John");
}

s
salsinga

Mockito returns objects but static means "class level,not object level"So mockito will give null pointer exception for static.


p
pete83

I seriously do think that it is code smell if you need to mock static methods, too.

Static methods to access common functionality? -> Use a singleton instance and inject that

Third party code? -> Wrap it into your own interface/delegate (and if necessary make it a singleton, too)

The only time this seems overkill to me, is libs like Guava, but you shouldn't need to mock this kind anyway cause it's part of the logic... (stuff like Iterables.transform(..))
That way your own code stays clean, you can mock out all your dependencies in a clean way, and you have an anti corruption layer against external dependencies. I've seen PowerMock in practice and all the classes we needed it for were poorly designed. Also the integration of PowerMock at times caused serious problems
(e.g. https://code.google.com/p/powermock/issues/detail?id=355)

PS: Same holds for private methods, too. I don't think tests should know about the details of private methods. If a class is so complex that it tempts to mock out private methods, it's probably a sign to split up that class...


Singleton will make you run into all sorts of problems, particularly when you realise that you actually need more than one instance and now you need to refactor your entire system to make that happen.
I did not say, that I recommend the Singleton Pattern to everyone. What I meant is, if I have to decide between a static utility class and a Singleton offering the same functionality, I'd choose the Singleton. And if a class is a Singleton or not should be controlled by the DI framework anyway, in my class I @Inject SomeDependency and in my configuration I define bind(SomeDependency.class).in(Singleton.class). Thus if tomorrow it's not a Singleton anymore, I change the one config and that's it.
@pete83 I hear you brother. However I have an issue with test libs or frameworks requiring devs to change their design to meet the test framework's design/limits. That is IMO putting the cart before the horse, or the tail wagging the dog.
That argument makes little sense to me. Singleton patterns have fallen out of favor for years now, for too many reasons to list here. What constitutes "clean" code? If I have a class instance method which calls a static helper method that returns some I/O operation, why would I not want that mocked in a test? And how is that poor design? All of this hand-wringing surrounding mocking static methods doesn't add up. Mocking a method is the opposite of testing it. If it's too hard to implement then just say that and be done with it
Oh man, I was never talking about that old school Singleton pattern where everybody calls Foo.getInstance() everywhere. I just wrote singleton in the answer to counter the argument "but a static method does not require the creation of many wrapper objects". Also conceptually to me there is little difference between a static method and an instance method on a singleton, just that you can't mock this singleton collaborator. But singleton or not is absolutely not the point I was trying to make, the point is to inject and mock collaborators and not call static methods if it makes testing hard.
P
PhillC

In some cases, static methods can be difficult to test, especially if they need to be mocked, which is why most mocking frameworks don't support them. I found this blog post to be very useful in determining how to mock static methods and classes.


Mocking of static methods is even easier than mocking of instances methods (since there is no instance), when using a suitable mocking API.
This is like answering the question with the question itself, which was why it is difficult to do so, to which this is not an answer.
I downvoted it because the blog post recommends a costly workaround (refactoring the production code), rather than actually solving the issue of isolating a class from the static methods that it happens to use. IMO, a mocking tool that truly does the job would not discriminate against methods of any kind; a developer should be free to decide whether the use of static methods is good or bad in a given situation, rather than being forced down one path.