6 ms·
As someone who is not in the Java world, why does Java need a mocking library? Interface based polymorphism is not enough?
by didip 9mo ago
As someone who is not in the Java world, why does Java need a mocking library? Interface based polymorphism is not enough?
- voidhorse 9mo agoArguably it doesn't. Mocking is over used and you should just use a real implementation or distributor provided fake whenever possible.
- wetpaws 9mo ago[dead]
- totallykvothe 9mo agoMockito allows one to write mocks in tests for code that doesn't use dependency injection and isn't properly testable in any other way. On the one hand, you should just design things to be testable from the start. On the other... I'm already working in this codebase with 20 years of legacy untestable design...
- deepsun 9mo agoGoogle API libraries mark every class as "final" so it's not trivial to mock-extend it for tests. But third-party IO is exactly the thing you'd want to mock. Probably because they zealously followed "Effective Java" book.
- wiseowise 9mo ago> But third-party IO is exactly the thing you'd want to mock. You write an adapter.
- deepsun 9mo agoNo, some other library classes accept only their own, not my adapter. Not mentioning of course needless copy-pasting dosens of members in the adapter. And it must be in prod code, not tests, even though it's documentation would say "Adapter for X, exists only for tests, to be able to mock X".
- wiseowise 9mo agoYou wrap whole 3rd party dependency in an adapter.
- akoboldfrying 9mo agoThat's a lot of upfront work and maintenance, not to mention the friction of needing to mentally translate every occurrence of OurFooAdapter to Foo in order to find documentation.
- wiseowise 9mo agoYeah, well, good code takes some thought to produce. More news at 11.
- deepsun 9mo agoThe second argument still holds -- all those wrappers will exist in prod only for tests. Moreover, that wrapper library is now a pretty large piece of code, and we'd want to maintain and test as well. But cannot without hacks.
- throwaway7375 9mo agoOnce you start writing adapters you need a way to instantiate them to choose between implementations, and factories is often used for this. Then you might generalize the test suites to make the setup easier and you end up with the infamous FactoryFactory pattern.
- deepsun 9mo agoI think it's easier to unpack/remove-"final"/re-compile the library .jar during the build time. Will be more stable to any changes than an adapter.
- gleenn 9mo agoThere are many cases where you don't control the library code your code depends on that you want to test. Also, the FactoryFactoryFactory patterns can be quite cumbersome and simply mocking out something makes for a far simpler test. There are likely more common cases.
- eoskx 9mo agoAs someone who has been out of Java for close to 10 years now, you certainly could do without Mockito, but you'd be writing a lot of boiler plate code repetitively. There's also the case of third-party libraries that you don't control and Mockito has decent facilities for working with those, especially when you're working with a codebase that isn't pure DI and interfaces.
- marginalia_nu 9mo agoThe point is to let you create mocks without having to go through the whole polymorphism rigmarole, without forcing classes to define a separate interface or anything like that.
- krackers 9mo agoMocks make it easy to record and assert on method invocations. Additionally spys (instance mocks) are really useful when you need to forward to the real method or rely on some state. At the moment I can't see anything Mokckito gives that you technically couldn't implement yourself via subclassing and overriding, but it'd be a lot of boilerplate to proxy things and record the arguments.
- sunnybeetroot 9mo agoSubclasing and overriding is not a good idea. There is no compilation failure if you forget to override a function which can lead to flakey tests at best and prod data impact at worst.
- wmichelin 9mo agoyour test environment should not have the credentials to write to prod data. yiiiiikes!
- sunnybeetroot 9mo agoCredentials end up existing in prod because the person used Mochito and didn’t override the function for providing credentials :’c
- senbrow 9mo agoCredentials should only be provided at the application root, which is going to be a different root for a test harness. Mockito shouldn't change whether or not this is possible; the code shouldn't have the prod creds (or any external resource references) hard coded in the compiled bytecode.
- sunnybeetroot 9mo agoI totally agree, I’m being tongue in cheek, but given how poor some codebases can be, the more precautions the better ie compilation failures on non-mocked functions.
- tripple6 9mo agoMockito uses declarative matching style of specifying what should be mocked. You don't need to implement or even stub all of interface methods since Mockito can do it itself. It may be extremely concise. For example, interfaces may have tens methods or even more, but only one method is needed (say, java.sql.ResultSet). And finally probably the most important thing, interaction with mocks is recorded and then can be verified if certain methods were invoked with certain arguments.
- derriz 9mo agoThat’s the seductive power of mocking - you get a test up and running quickly. The benefit to the initial test writer is significant. The cost is the pain - sometimes nightmarish - for other contributors to the code base since tests depending on mocking are far more brittle. Someone changes code to check if the ResultSet is empty before further processing and a large number of your mock based tests break as the original test author will only have mocked enough of the class to support the current implementation. Working on a 10+ year old code base, making a small simple safe change and then seeing a bunch of unit tests fail, my reaction is always “please let the failing tests not rely on mocks”.
- wpollock 9mo ago> Someone changes code to check if the ResultSet is empty before further processing and a large number of your mock based tests break as the original test author will only have mocked enough of the class to support the current implementation. So this change doesn't allow an empty result set, something that is no longer allowed by the new implementation but was allowed previously. Isn't that the sort of breaking change you want your regression tests to catch?
- akoboldfrying 9mo agoIt doesn't have to be a breaking change -- an empty result set could still be allowed. It could simply be a perf improvement that avoids calling an expensive function with an empty result set, when it is known that the function is a no-op in this case.
- davnicwil 9mo agobecause even supposing you have an interface for your thing under test (which you don't necessarily, nor do you necessarily want to have to) it lets you skip over having to do any fake implementations, have loads of variations of said fake implementations, have that code live somewhere, etc etc. Instead your mocks are all just inline in the test code: ephemeral, basically declarative therefore readily readable & grokable without too much diversion, and easily changed. A really good usecase for Java's 'Reflection' feature.
- BlackFly 9mo agoAn anonymous inner class is also ephemeral, declarative, inline, capable of extending as well as implementing, and readily readable. What it isn't is terse. Mocking's killer feature is the ability to partially implement/extend by having some default that makes some sense in a testing situation and is easily instantiable without calling a super constructor. Magicmock in python is the single best mocking library though, too many times have I really wanted mockito to also default to returning a mock instead of null.
- davnicwil 9mo ago> What it isn't is terse Yeah, it's funny, I'm often arguing in the corner of being verbose in the name of plain-ness and greater simplicity. I realise it's subjective, but this is one of the rare cases where I think the opposite is true, and using the 'magic' thing that shortcuts language primitives in a sort-of DSL is actually the better choice. It's dumb, it's one or two lines, it says what it does, there's almost zero diversion. Sure you can do it by other means but I think the (what I will claim is) 'truly' inline style code of Mockito is actually a material value add in readability & grokability if you're just trying to debug a failing test you haven't seen in ages, which is basically the usecase I have in mind whenever writing test code.
- BlackFly 9mo agoI cannot put my finger on it exactly either. I also often find the mocking DSL the better choice in tests. But when there are many tests where I instantiate a test fixture and return it from a mock when the method is called, I start to think that an in memory stub would have been less code duplication and boilerplate... When some code is refactored to use findByName instead of findById and a ton of tests fail because the mock knows too much implementation detail then I know it should have been an in memory stub implementation all along.
- wiseowise 9mo agoIt doesn't. But good luck teaching hordes of enterprise "developers".
- throwaway7375 9mo agoBefore Mockito, it was common (where I worked) to create an interface just to support testing. This is an anti-pattern in my opinion. To create interfaces just for testing complicates the code and it is one of my pet peeves. It also encourages the factory pattern. I prefer Mockito's approach.
- tizzy 9mo agoIt’s definitely a bit annoying and verbose in Java but I think creating an interface to support testing is a net positive. That interface is the specification of what that concrete class requires it’s dependencies to do. I think all the dependencies of a class should define behaviour not implementation so it’s not tightly coupled and can be modified in the future. If you have a class that injects LookUpService, why not put an interface LookUpper in front of it? It’s a layer of indirection but we have IDEs now and reading the interface should be easier or at least provide context.