Why doesn't Mockito mock static methods?
Asked Answered
T

7

298

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.

Teethe answered 19/12, 2010 at 9:25 Comment(2)
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.Resurrectionism
Mockito [3.4.0] can mock static methods!Mulley
R
259

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...

Resurrectionism answered 19/12, 2010 at 9:43 Comment(8)
The same is true for mocking constructors by the way. Those, too, cannot be changed via inheritance.Resurrectionism
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/…Resurrectionism
actually I just realized Jan already mentioned this in his answer, the post I linked is still worth reading thoughResurrectionism
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.Ammoniate
Which framework should I then use with Android? I like to mock statics and private methods.Bernal
PowerMock? It mocks static methods.Resurrectionism
@Ammoniate - 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"...Hardened
@Hardened 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.Ammoniate
E
25

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)?

Elmaleh answered 19/12, 2010 at 18:4 Comment(6)
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.Candlewick
And why do you have to mock it? If you depend on the static method, you "unit" or "module" is not the class alone but also includes the "static persistence facade".Elmaleh
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.Lituus
True, but sometimes we may be dealing with singletons.Legion
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...Rawboned
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.Manon
M
20

Mockito [3.4.0] can mock static methods!

  1. Replace mockito-core dependency with mockito-inline:3.4.0.

  2. Class with static method:

    class Buddy {
      static String name() {
        return "John";
      }
    }
    
  3. Use new method Mockito.mockStatic():

    @Test
    void lookMomICanMockStaticMethods() {
      assertThat(Buddy.name()).isEqualTo("John");
    
      try (MockedStatic<Buddy> 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.

Mulley answered 16/7, 2020 at 1:9 Comment(5)
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.Sungkiang
@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 ?Binominal
@Binominal 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'dMulley
@Binominal ...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.Mulley
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 helpStupefacient
G
6

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");
}
Giacobo answered 7/9, 2020 at 14:58 Comment(2)
Does it work for static method from a abstract class?Kalle
@Kalle yes it doesGiacobo
J
4

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

Jolenejolenta answered 27/11, 2014 at 4:49 Comment(0)
D
3

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...

Despicable answered 20/7, 2014 at 23:8 Comment(6)
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.Haught
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.Despicable
@Despicable 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.Believe
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 itLeena
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.Despicable
And as to tests not forcing devs to change their design, maybe design should follow the goal to be testable. Of course I can say "but I as the developer am the boss, so I wire up and connect this database driver in the static initializer of my class". But I would not want to write a test framework to support rewriting the low level static initializer byte code, just because someone chose this design over a testable approach. And as a dev you shouldn't complain if your hard core byte code rewriting test framework then causes all kind of weird errors as soon as you do a simple minor JDK upgrade.Despicable
C
0

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.

Cachinnate answered 19/12, 2010 at 9:35 Comment(3)
Mocking of static methods is even easier than mocking of instances methods (since there is no instance), when using a suitable mocking API.Candlewick
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.Resurrectionism
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.Candlewick

© 2022 - 2024 — McMap. All rights reserved.