Live data from Hacker News

Write tests. Not too many. Mostly integration

blog.kentcdodds.com

121–130 of 338 posts

Re: Write tests. Not too many. Mostly integration

#121

Earlier quoted context omitted.

Simply because you can't mock the static dependency, therefore that method is now dependent on the static class and you don't have any control over it. This is problematic - what if at some point later another developer adds a database call into the static method to do some logging? Now your testing will dirty whatever database you're using, as well as run 10x slower - and yet the test will still pass and everyone wi…

> that method is now dependent on the static class and you don't have any control over it. I don't see how you have any less control over it than any other code you wrote. If you don't want it to write log statements, then don't do that. Most static methods are small and pure so don't need to write log statements anyway. > Now your testing will dirty whatever database you're using, as well as run 10x slower. I've nev…

I think you missed my point, it's not about the logging framework, its about the fact you don't control an external dependency during testing. Unit tests are meant to be reproducible, meaning they are done under controlled conditions.

> Most static methods are small and pure

This is very assuming, tests are a way of being specific about your intent.

Re: Write tests. Not too many. Mostly integration

#122

Earlier quoted context omitted.

Simply because you can't mock the static dependency, therefore that method is now dependent on the static class and you don't have any control over it. This is problematic - what if at some point later another developer adds a database call into the static method to do some logging? Now your testing will dirty whatever database you're using, as well as run 10x slower - and yet the test will still pass and everyone wi…

How is static code different from other noninjected code, like stuff in a method. Taken to the logical conclusion we'll have thousands or classes full of max 2 operations per method.

How many static classes are your methods using? And what is the problem with injecting this stuff at the top of the class instead? If you plan to write tests, you have to control your dependencies, and DI is the simpliest way to do that.

Re: Write tests. Not too many. Mostly integration

#123
post #93

Earlier quoted context omitted.

I don't see why TDD requires ruling out static methods and insisting on hiding everything behind an interface. Static methods are straightforward to test, certainly more than a class with multiple dependencies which need to be mocked. Usually the complaint is about coupling when calling static methods but these can be wrapped in a delegate if required.

Problem is that the moment you start introducing delegates and crap like that is you're inventing a mechanism to work around your resistance to not using static methods rather than actually solving any problems. There is no functional difference between a class with static methods and a class without, of which one instance is available to other classes. Other than the fact that it isolates state, allows mocking and s…

I disagree that delegates and higher-order function are 'crap' or in any way more complicated than introducing interfaces that are injected though a centralised container. You could just as easily turn that argument around and say mocking and an overuse of interfaces come from your resistance to using small static methods. In C# Linq is almost entirely based on static methods and delegates and that is not harder to test as a result.

Static methods usually don't rely on any hidden state at all. The example originally given was for a graph operation which could just take the input graph as an argument and return the result. When your code is composed of small independent functions you don't need mocking and substitution at all. In my experience most uses of mocks come from functions that do too much in the first place.

Re: Write tests. Not too many. Mostly integration

#124
Clean architecture: https://smile.amazon.com/Clean-Architecture-Craftsmans-Softw...

Anything that touches the real world should be as small as possible.

I've been writing code using tests for about 4+ years and I now can't think of writing code any other way.

I would be scared of refactoring. Also I'm testing the code anyway - why not write it down so it gets done every time?

Run integration tests too for sanity - they're testing the integration of things.

To me: coding without tests is like going caving without a flash light. You don't really know what your code does until you run tests. Your confidence rises when more code is covered.

No it isn't perfect - but not writing tests is not better.

Re: Write tests. Not too many. Mostly integration

#125

Earlier quoted context omitted.

> The biggest problem I see with people trying to write unit tests is that they don’t want to change how they write code. They just want tests for it. It’s like watching an OO person try their hardest to write OO code in a functional language. The biggest problem I see with people advocating for tests and employing TDD is that they do change how they write code to accommodate tests. This leads to inclusion of lots of…

>That said, if you go for functional style in OOP, i.e. shoving as much as you can into static helper functions and most of the rest into dumb private stateless functions, you suddenly gain both a clean architecture and lots of test points to use in unit tests. So you can have testable code, but you have to chill out with the OOP thing a bit. Wow, this is exactely totally opposite of how one can achieve testability i…

I'm not sure if this article is clever satire.

> The basic issue with static methods is they are procedural code.

So is any object-oriented code. OOP is a subparadigm of procedural programming.

> Unit-testing needs seams, seams is where we prevent the execution of normal code path and is how we achieve isolation of the class under test. seams work through polymorphism, we override/implement class/interface and than wire the class under test differently in order to take control of the execution flow. With static methods there is nothing to override.

Why did it not occur to him that the function boundary is the "seam" he's trying to find?

I mean, `method(a, b)` is equivalent (as in: equally expressive, and usually implemented in the same way) as `a.method(b)`. Therefore, any problems with one case equally apply to the other case. If his problem is that `method(a, b)` may call other, non-mockable functions, then that criticism equally applies to `a.method(b)`.

(As I'm writing this, it occurs to me that the author may be suffering from the "OOP = Java" delusion.)

Re: Write tests. Not too many. Mostly integration

#126

Earlier quoted context omitted.

people ... don’t want to change how they write code. They just want tests for it Have you considered the possibility that those people are right? That's a reasonable conclusion to make if you are seeing lots of otherwise smart people that share an opinion that disagrees with yours. There are lots of valid reasons to change the style in which you write code. In my mind, fitting somebody's fad testing scheme is not one…

I strongly agree with that, too. My current, experience-born belief is that if the only reason for introducing some architectural pattern is to accommodate testing better, the change is wrong and will likely hurt the code quality. Yes, you need to concede a little bit to allow for test points, but turning your code inside-out to have it go through three layers of indirection so that the middle one can be mocked easil…

yea, you should be changing code style to increase modularity in a way that is conceptually coherent in terms of what is easy to hold in your head. Increased testability should fall out of that because you can think through "What invariant should hold true about X under conditions/inputs Y1...Y4?"

Re: Write tests. Not too many. Mostly integration

#127

Earlier quoted context omitted.

> To liken it to something you may be familiar with - when commenting your code, do you think it's better to add comments in as you write the code? Or add in the comments at a later date after the code is all written? I'm sure you immediately know which approach results in better quality commenting, and it's the same with TDD. Not to take the analogy too far, but usually when writing a chunk of code I can keep it's b…

People just don't write comments or tests after, that's the problem. If you do then that's fine, but after trying both routes I actually find TDD to feel like less work - not having to wait on large build times and manually navigating the UI actually makes for a more fun experience. Instant feedback being the fun part. Additionally writing tests 'after' always feels like work to me and I end up hating it, especially…

> People just don't write comments or tests after, that's the problem.

Doesn't that get caught in code review anyway though? I find being forced to write tests first can be clunky and inefficient. Also, I've worked with people who insist on the "write the minimum thing that makes the test pass" mantra which I find really unnatural like you're programming with blinkers on. TDD takes the fun out of coding for me sometimes.

Generally I'd rather sketch out a chunk of the code to understand the problem space better, figure out the best abstractions, clean it up then write tests that target the parts that are most likely to have bugs or bugs that would have the biggest impact.

I find when you're writing tests first, you're being forced to write code without understanding the problem space yet and you don't have enough code yet to see the better abstractions. When you want to refactor, you've now got to refactor your tests as well which creates extra work which discourages you from refactoring. When the behaviour of the current chunk of code you're working on can still be kept in your head, I find the tests aren't helping all that much anyway so writing tests first can get in the way.

Re: Write tests. Not too many. Mostly integration

#128

Earlier quoted context omitted.

One reason why is because you should be testing the public behavior of a function/class not the details. The reason for this is because the public interface is what other parts of the codebase will come to rely on. Refactoring generally shouldn’t change the public interface as it will break other pieces of code within your codebase, or other codebases if it’s a library, and other systems if it’s a network api. So, if…

I would argue you absolutely need to be testing the internal details. That is the entire point of measuring branch coverage and performing mutation testing. Unit tests are not black box tests. They need to know that for special values, the unit has to follow a different code path but still produce sensible output. Reading the documentation of a function is not sufficient to determine what edge cases the unit has, but…

> the number of tests would explode exponentially with the number of branch conditions to handle all the corner cases

Then wouldn't you want to write something that was able to iterate through those edge-case interactions and ensure they are correct?

Re: Write tests. Not too many. Mostly integration

#129
> I’ve heard managers and teams mandating 100% code coverage for applications. That’s a really bad idea. The problem is that you get diminishing returns on our tests as the coverage increases much beyond 70%...

I call bullshit.

I work on V8, on JITs and WebAssembly. 70% coverage for these code bases would be absurdly low. We would never ship code that is that poorly tested, and you shouldn't either.

> You may also find yourself testing implementation details just so you can make sure you get that one line of code that’s hard to reproduce in a test environment. You really want to avoid testing implementation details because it doesn’t give you very much confidence that your application is working and it slows you down when refactoring. You should very rarely have to change tests when you refactor code.

What in the. serious. fuck. Of course tests test implementation details. Because _implementation details_ are where the goddamn bugs are.

> ... Maintaining tests like this actually really slow you and your team down.

That's the whole _point_. It slows you down in the short term but it keeps you from experiencing a full-on system meltdown when everything seems to be breaking at once.

Please don't follow the advice of this. It's total crap.

If you've never worked on a system that has survived more than 3 years, sure, go right ahead, run against the wall. But when you work on a system survives 5, 10 (V8), or 20 years (HotSpot JVM), then you really, really want to have good tests.

Re: Write tests. Not too many. Mostly integration

#130

This is my version: Don't stop writing tests. Not until 100% coverage. Mostly unit. Especially if you are developing a library. An untested code branch is a ticking time bomb, in my book. An application for the end users indeed does benefit from integration tests, a lot. The problem is running them efficiently. If they take an hour to run, nobody will care to analyse them.

Analyse? Shouldn't tests just pass?

After a point they just fix them to make them pass. Integration tests are really easy to cheat when the people writing them and using them are both really demotivated.
Post reply on HN