Hacker Newsnew | past | comments | ask | show | jobs | submit | derriz's commentslogin

That’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”.


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


I used ResultSet because the comment above mentioned it. A clearer example of what I’m talking about might be say you replace “x.size() > 0” with “!x.isEmpty()” when x is a mocked instance of class X.

If tests (authored by someone else) break, I now have to figure out whether the breakage is due to the fact that not enough behavior was mocked or whether I have inadvertently broken something. Maybe it’s actually important that code avoid using “isEmpty”? Or do I just mock the isEmpty call and hope for the best? What if the existing mocked behavior for size() is non-trivial?

Typically you’re not dealing with something as obvious.


What is the alternative? If you write a complete implementation of an interface for test purposes, can you actually be certain that your version of x.isEmpty() behaves as the actual method? If it has not been used before, can you trust that a green test is valid without manually checking it?

When I use mocking, I try to always use real objects as return values. So if I mock a repository method, like userRepository.search(...) I would return an actual list and not a mocked object. This has worked well for me. If I actually need to test the db query itself, I use a real db


The alternative to what? Using mocks?

For example, one alternative is to let my IDE implement the interface (I don’t have to “write” a complete implementation), where the default implementations throw “not yet implemented” type exceptions - which clearly indicate that the omitted behavior is not a deliberate part of the test.

Any “mocked” behavior involves writing normal debuggable idiomatic Java code - no need to learn or use a weird DSL to express the behavior of a method body. And it’s far easier to diagnose what’s going on or expected while running the test - instead of the backwards mock approach where failures are typically reported in a non-local manner (test completes and you get unexpected invocation or missing invocation error - where or what should have made the invocation?).

My test implementation can evolve naturally - it’s all normal debuggable idiomatic Java.


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

If it's not a breaking change, why would a unit test fail as a result, whether or not using mocks/fakes for the code not under test? Unit tests should test the contract of a unit of code. Testing implementation details is better handled with assertions, right?

If the code being mocked changes its invariants the code under test that depends on that needs to be carefully re-examined. A failing unit test will alert one to that situation.

(I'm not being snarky, I don't understand your point and I want to.)


The problem occurs when the mock is incomplete. Suppose:

1. Initially codeUnderTest() calls a dependency's dep.getFoos() method, which returns a list of Foos. This method is expensive, even if there are no Foos to return.

2. Calling the real dep.getFoos() is awkward, so we mock it for tests.

3. Someone changes codeUnderTest() to first call dep.getNumberOfFoos(), which is always quick, and subsequently call dep.getFoos() only if the first method's return value is nonzero. This speeds up the common case in which there are no Foos to process.

4. The test breaks because dep.getNumberOfFoos() has not been mocked.

You could argue that the original test creator should have defensively also mocked dep.getNumberOfFoos() -- but this quickly becomes an argument that the complete functionality of dep should be mocked.


Jumping ahead to the comments below: obviously, I mentioned `java.sql.ResultSet` only as an example of an extremely massive interface. But if someone starts building theories based on what is left unsaid in the example for those from outside the Java world, one could, for instance, assume that such brittle tests are simply poorly written, or that they fail to mitigate Mockito's default behavior.

In my view, one of the biggest mistakes when working with Mockito is relying on answers that return default values even when a method call has not been explicitly described, treating this as some kind of "default implementation". Instead, I prefer to explicitly forbid such behavior by throwing an `AssertionError` from the default answer. Then, if we really take "one method" literally, I explicitly state that `next()` must return `false`, clearly declaring my intent that I have implemented tests based on exactly this described behavior, which in practice most often boils down to a fluent-style list of explicitly expected interactions. Recording interactions is also critically important.

How many methods does `ResultSet` have today? 150? 200? As a Mockito user, I don't care.


Drives me crazy as I travel a lot. Even if I‘m logged into Google and it’s been tracking me for years and it‘s still targeting me with obviously personalized ads but it will assume I’ve suddenly acquired temporary proficiency in French, German, Italian, Spanish, etc stubbornly ignoring the language I’ve used for my query or the only language I’ve ever - in all my interactions with Google over years - ever used. What the fuck would be so hard to just NOT use braindead geolocation (which doesn’t even work in countries like Switzerland) when you know for certain who I am. You track all this information about me and use it to generate ad revenue but refuse to use my language preference?

Patrick Boyle eventually comes to a similar conclusion in his video about global population decline - https://youtu.be/ispyUPqqL1c?si=7jUgVBkOvLHluPAR - but includes lots of graphs and other interesting factlets.

* warning for Americans: not suitable for those offended by sarcasm


I don’t see the relevance to the topic. I could preface your list with something like “The monkey wrench is not the best tool for the following situations:”. It’s kinda vacuously true in a meaningless way but without expansion adds nothing to a discussion about the relative merits of monkey wrenches versus other similar tools like pliers or vice grips.

> Most local hackerspaces I visited are basically green and leftist queer safe spaces where adults run around with stuffed animals.

So what? You’re not being asked or expected to feel empathy - just show tolerance. Which is the easiest virtue to develop - just ignore behavior which doesn’t threaten you.

If someone is doing their own thing - wearing a MAGA hat, a rainbow t-shirt or carrying a fluffy toy - it doesn’t bother me in the slightest. Why does it bother you unless they’re getting in your face?


What is "so what" supposed to mean that isn't covered already by the exact sentence afterwards? Why even come at me with "tolerance" when that's exactly what I haven't been receiving, as I laid out in the paragraph you selectivity quoted. What's your point exactly?


> Important note: the strange magic requires the official git completion script.

I dislike the official git completion script because it’s so slow when working with large repos. On macOS - because of vagaries of the way its file system works - with large repos it’s effectively unusable. Hint to completion script writers: if your “smart” completion ever takes seconds, it’s worse than useless and far worse than “dumb” filename completion.


What puts me off the Amiga scene is how little of the 30 or 40 year old software has been released into the public domain.

I have no issue with charging for new software for retro platforms in order to support the scene but find it slightly offensive that you have to pay to acquire a legal 1.3 kickstart ROM - released and unchanged since 1988. It just feels like copyright squatting.


Yeah but that's probably because the legal blocks are a bit complicated. Most people just pirate it, I'd assume. I find all that stuff on torrents if I need.


Also banned: “Imran Ahmed, the British CEO of the US-based Center for Countering Digital Hate; Anna-Lena von Hodenberg and Josephine Ballon of the German non-profit HateAid; and Clare Melford, co-founder of the Global Disinformation Index”

The bullies are anti-disinformation and anti-hate speech activists?

The USA using its diplomatic power to become a defender of hate-speech and disinformation?

I guess when JD Vance deliberately snubbed the democratically elected government of the country when visiting Germany and instead spent his time with a far-right neo-nazi party, it should have been clear which way the wind was blowing.


If you still in 2025 don't understand that hate speech and anti-disinformation is a trick used by people who want to forcefully censor others who don't agree with them then I don't know what to tell you.


Yeah, I’ve always felt that the stream API is a leaky abstraction for providing access to networking. I understand the attraction of making network I/O look like local file access given the philosophy of UNIX.

The API should have been message oriented from the start. This would avoid having the network stack try to compensate for the behavior of the application layer. Then Nagel’s or something like it would just be a library available for applications that might need it.

The stream API is as annoying on the receiving end especially when wrapping (like TLS) is involved. Basically you have to code your layers as if the underlying network is handing you a byte at a time - and the application has to try to figure out where the message boundaries are - adding a great deal of complexity.


the whole point of TCP is that it is a stream of bytes, not of messages.

The problem is that this is not in practice quite what most applications need, but the Internet evolved towards UDP and TCP only.

So you can have message-based if you want, but then you have to do sequencing, gap filling or flow control yourself, or you can have the overkill reliable byte stream with limited control or visibility at the application level.


For me, the “whole point” of TCP is to add various delivery guarantees on top of IP. It does not mandate or require a particular API. Of course, you can provide a stream API over TCP which suits many applications but it does not suit all and by forcing this abstraction over TCP you end up making message oriented applications (e.g request /response type protocols) more complex to implement than if you had simply exposed the message oriented reality of TCP via an API.


TCP is not message-oriented. Retransmitted bytes can be at arbitrary offsets and do not need to align with the way the original transmission was fragmented or even an earlier retransmission.


I don’t understand your point here or maybe our understanding of the admittedly vague term “message oriented” differs.

I’m not suggesting exposing retransmission, fragmentation, etc to the API user.

The sender provides n bytes of data (a message) to the network stack. The receiver API provides the user with the block of n bytes (the message) as part of an atomic operation. Optionally the sender can be provided with notification when the n-bytes have been delivered to the receiver.


Is this a TCP API proposal or a protocol proposal?

Because TCP, by design, is a stream-oriented protocol, and the only out-of-band signal I'm aware of that's intended to be exposed to applications is the urgent flag/pointer, but a quick Google search suggests that many firewalls clear these by default, so compatibility would almost certainly be an issue if your API tried to use the urgent pointer as a message separator.

I suppose you could implement a sort of "raw TCP" API to allow application control of segment boundaries, and force retransmission to respect them, but this would implicitly expose applications to fragmentation issues that would require additional API complexity to address.


I think you're misunderstanding their point.

Your API is constrained by the actual TCP protocol. Even if the sender uses this message-oriented TCP API, the receiver can't make any guarantees that a packet they receive lines up with a message boundary, contains N messages, etc etc, due to how TCP actually works in the event of dropped packets and retransmissions. The receiver literally doesn't have the information needed to do that, and it's impossible for the receiver to reconstruct the original message sequence from the sender. You could probably re-implement TCP with retransmission behaviour that gives you what you're looking for, but that's not really TCP anymore.

This is part of the motivation for protocols like QUIC. Most people agree that some hybrid of TCP and UDP with stateful connections, guaranteed delivery and discrete messages is very useful. But no matter how much you fiddle with your code, neither TCP or UDP are going to give you this, which is why we end up with new protocols that add TCP-ish behaviour on top of UDP.


Fair enough - I didn’t really fully consider the effect of retransmission and segmentation on the receiver view.


> message oriented

Very well said. I think there is enormous complexity in many layers because we don't have that building block easily available.


It's the main reason why I use websockets for a whole lot of things. I don't wanna build my own message chunking layer on top of TCP every time.


WebSocket is full of web-tech silliness; you'd be better off doing your own framing.


Well, it also has the advantage of providing pretty decent encryption for free through WSS.

But yeah, where that's unnecessary, it's probably just as easy to have a 4-byte length prefix, since TCP handles the checksum and retransmit and everything for you.


It's just a standard TLS layer, works with any TCP protocol, nothing WebSocket-specific in it.

You should ideally design your messages to fit within a single Ethernet packet, so 2 bytes is more than enough for the size. Though I have sadly seen an increasing amount of developers send arbitrarily large network messages and not care about proper design.


Meh I've worked enough with OpenSSL's API to know that I never ever want to implement SSL over TCP myself. Better let the WebSocket library take care of it.


Agree that Pandas is horribly irregular - the only worse query language I’ve had to work with is Mongo’s. After about a decade of regular Pandas use, switching to Polars was such a relief. It’s not perfect since it’s slightly limited by being a Python library rather than an embedded query language but it’s so much better designed than Pandas - even ignoring the huge performance improvement. In my circle, Pandas is being abandoned en mass for Polars.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: