Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
I Mock Your Mocks (boot.dev)
16 points by wg0 on July 19, 2023 | hide | past | favorite | 13 comments


The author fails to justify why he "hates mocks" except a hand-waving justification of "they don't test production code". Well, they do, other than the parts that are mocked. They "don't test production code" in the same way that his unit test doesn't test production code because it only touches a fraction of the real code.

I don't disagree with his decomposition, it's correct. I also agree with his approach to unit and integration testing to touch the appropriate layers. It's just his knee-jerk and unjustified hatred of mocks I'm objecting to.


Yeah. I completely agree with the part about separating db stuff and logic (it's a huge pet peeve of mine and almost nobody at work does anything like that), but I think mocks do have a place.

I also think it's best to make sure the majority of our code doesn't require mocks to test. But very few people do that either even if it's fairly simple.

I am fairly fresh with my two years of professional experience and I've spent a lot of energy worrying about whether there's something wrong with me that makes me want to refactor most of the code I read. I'm starting to lean towards the conclusion that it's not my fault, it's just that most devs write shitty code.

And I don't think that's javas fault.


For all the hatred of mocks, it seemed strange that the article spent so little time actually talking about them.


Yeah, there are so many reasons not to like mocks and he picked… that one?


honestly, with mocks and good type checking, I don't see why it wouldn't be valid testing


One tangential complaint:

    func validateUser(user User) error
This is a bad way to validate data. It generally means that any new data added to this type won't be validated by default. It isn't too bad because it is a typed native data structure (must worse in things like JS where extra fields can slip in from the JSON) but it is better security practice to parse the data rather than just looking at it. So it would look something like this

    func validateUser(user User) (error, User)
Then the function carefully extracts and validates the input before copying it to the output. Anything that it doesn't know about doesn't get copied. This can be thought of as "whitelist validation" rather than "blacklist validation".

Parse, don't validate.


if you're going to "parse, don't validate" then the signature should probably be

func validateUser(user UnvalidatedUser) (error, User)

this way, you can't accidentally forget to validate your user before using it in the rest of your code


If we're going that route, just forbid making User objects which are not valid. Hide the fields, force the instances to be immutable, validate in the constructor, problem solved. Does not work in things with duck typing or anything remotely resembling the two most used programming languages in new projects in the industry. But for traditional statically typed languages, it's a good solution. Now a function taking in a user is literally incapable of ever taking in an invalid user, assuming your tests for the class are good and pass.


I have always liked this approach and, recently, I came across a term for it: "type tightness"[0]. I gotta say, it really stuck with me.

[0]: https://www.ecorax.net/tightness/


Exactly - if you have Xs that are foolike, and Xs that are barlike, the perhaps you should have Yz and Zs instead.

Further, if you want to validate that your X is barlike, all you have to do now is make sure you have some function f : Y -> Z. If you want to scrutinize your validation logic, then you need only look at all functions of that specific type in your program.

Admittedly, go is probably not a language with the best support for these kinds of constructs.



I've found the best to test everything from the http handler to database in one test. Unless you want to write 5 different tests for each endpoint. In one project we've run the tests against in-memory db and production was postgres. Of course there was a bug caused by different SQL implementations. Other time, we've tested only the the service layer (the fiddling part worth testing), skipping the spring boot controller. Of course there was a bug caused by bad annotations at the controller layer.


How will you ever achieve 100% code coverage if you don't have giant monofunctions?




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

Search: