News for hackers. Stuff that matters.
Re:Write tests. Not too many. Mostly integration by TeMPOraL on Friday October 27, 2017 @03:50AM (#15566460)
> 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 unnecessary abstraction and boilerplate patterns that make code less readable and more bug-prone. OO world has spawned numerous non-solutions to turn your code inside-out so that it's easier to mock things, at the expense of code quality itself.

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.

Re:Write tests. Not too many. Mostly integration by positivecomment on Friday October 27, 2017 @04:28AM (#15566613)
> as much as you can into static helper functions and most of the rest into dumb private stateless functions

In our work we use C# and it is very hard, even next to impossible to make a static class pass a code review - given it's not for extension methods (which I hate... why not be explicit about the first parameter and stop acting as a part of the class </rant>). They just tell us to use IoC and move to the next point. I honestly don't know why. Our IoC library can treat a dependency as static or singleton, but those are also discouraged. Once I had a static class named GraphRequestHelpers* and the reviewer got really negative, FSM knows why. She told me that we need IoC to make everything testable and "Helper" in the name is a code-smell. Sounds cargo-culting to me but I have only 6 years of experience so who I am to know.

* Now we have RequestExtensions and everything is apparently perfect.

Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @05:55AM (#15566987)
There is some cargo culting there but it's mostly correct.

Helper is a code smell as it's a sign of "we don't know what the responsibility of this is or what to call it so we'll just chuck a load of shit in this file and call it a helper". The methods in should belong to something and live on that class, not in an external class.

RequestExtensions is more shit than the original solution. Extension methods are even worse! Shoot the reviewer.

Re:Write tests. Not too many. Mostly integration by quickthrower2 on Friday October 27, 2017 @06:28AM (#15567106)
This is a matter of taste not fact. In functional languages the style is compositional with static functions everywhere. It works well. The keeping data and methods together thing is one approach. Sometimes it's great. Sometimes unnecessary.

For example would you argue against string formatting helpers? Or would they need to be written to an interface and added to myriad DI bucket lists?

Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @07:00AM (#15567229)
It's not that simple and it's not a fact. I'm an advanced user of functional languages as well and have written an entire scheme implementation before. I only semi-agree. That's slightly disingenuous representation of functional languages which have more than a few pitfalls. They certainly aren't the silver bullet and they really do not scale to the same height and complexity of the problem domain as the OO languages do due to the nature of the abstraction you describe. Nothing is particularly explicit. I'd rather take the compromises of OO over the maintenance problems of a functional language.

String formats are data so they would be stored as constants so that they are interned. They can be stored in a const class which is a static class with no methods i.e.:

   sealed class StringFormats {
       public const DateFormatX = @"...";
   }
Also string formats for example tend to be owned by the respective objects so you can add overloads to the object to provide certain arbitrary representations. If the translation between an object and the string representation is complex, then you're really serializing it so that should be an abstracted concern.
Re:Write tests. Not too many. Mostly integration by quickthrower2 on Friday October 27, 2017 @04:47PM (#15572102)
To be clear I'm not saying functional is always better. I'm saying there are other possibilities and dogma is bad.

The Haskell community has its dogmas too. And it's fair share of "let's do this simpler" blog posts.

As an aside Haskell has many equivalents of dependency injection and sugars to help and you can "inject all the things" there too.

My point is to think of the problem you are trying to solve, rather than ticking off the SOLID / Martin Fowler etc. tick boxes.

Understanding OO patterns, SOLID etc is a good thing but being prepared to "break the rules" is good IMO too.

Re:Write tests. Not too many. Mostly integration by cjsuk on Saturday October 28, 2017 @06:03AM (#15574591)
Breaking the rules is good when appropriate. Problem is those rules are pretty amazingly good. I went through a weird phase of denial and ended up back where I started before I applied the aforementioned rules.

Every exception I have shot myself.

Re:Write tests. Not too many. Mostly integration by positivecomment on Friday October 27, 2017 @06:02AM (#15567012)
> Shoot the reviewer

Duly noted! Although I'll try talking to her first, I'm sure there's more behind the decision :)

One of the methods that was inside takes a request, extracts the body and returns the parsed graph from the body. It's used by many controllers from many projects. I don't know where to put such a thing, hence the request extension.

Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @06:06AM (#15567026)
Always ask for the reason before slating it :)

Usually that's a single responsibility class:

   interface IGraphParser {
       Graph Parse(Request request);
   }
Inject that into the caller via the container then you can mock the thing that calls it and just return a static Graph object, which you can't do with a simple extension method (which is why it sucks).
Re:Write tests. Not too many. Mostly integration by seanmcdirmid on Friday October 27, 2017 @08:59AM (#15567838)
Extension methods are useful for only one reason: they trigger code completion for browsing what this object can do. Static methods suffer from FP code completion problems (you can’t complete easily in the first arg of a function/procedure).
Re:Write tests. Not too many. Mostly integration by emodendroket on Friday October 27, 2017 @11:01AM (#15569015)
I think I am not mistaken in saying extension methods, like lambda functions, were invented primarily for the use case of Linq. Even if they weren't, that's how Linq is implemented, so extension methods serve more than that "one purpose" if you don't insist on writing C# in the style of C# 2.0.
Re:Write tests. Not too many. Mostly integration by seanmcdirmid on Friday October 27, 2017 @07:42PM (#15573142)
They came out at the same time, I’m sure there was some influence between them (Mads Tergesen would know better). However, all the functionality added in could have been done with static methods, just with more verbose syntax. LINQ query syntax could have been special cases. Anyways, I like what they came up with, it’s very versatile.
Re:Write tests. Not too many. Mostly integration by emodendroket on Friday October 27, 2017 @09:16PM (#15573473)
It's not clear that would have been much less work
Re:Write tests. Not too many. Mostly integration by emodendroket on Friday October 27, 2017 @10:59AM (#15569003)
Why hate extension methods? Do you really want to write Enumerable.ToList(Enumerable.Select(Enumerable.Where(someList, e => e.someBool), e => new {a = e.x, b = e.y)) and so on?
Re:Write tests. Not too many. Mostly integration by positivecomment on Friday October 27, 2017 @11:19AM (#15569235)
That would suck, on the other hand, the extension methods make people create huge chains continuations of which comes from who knows where.

Best solution would have been a pipe operator if you ask me.

Re:Write tests. Not too many. Mostly integration by pjc50 on Friday October 27, 2017 @05:42AM (#15566918)
Could someone expand IoC for me please?
Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @05:47AM (#15566945)
Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @04:58AM (#15566748)
Do you practise TDD? If you did a lot of this would make more sense to you. TDD is actually quite fun when you get the hang of it (less mental burden as you push all the 'intent' onto the computer).
Re:Write tests. Not too many. Mostly integration by lkitching on Friday October 27, 2017 @05:23AM (#15566844)
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.
Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @05:30AM (#15566867)
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 will be none the wiser as to what happened.

If you start using a custom delegate solution, then your code is not consistent with everything else that uses DI, making it harder to understand. I can understand interfaces are annoying when navigating code, but the IDE still helps with that even if it is a few more button clicks, and the pros outweigh the cons.

Re:Write tests. Not too many. Mostly integration by lkitching on Friday October 27, 2017 @05:57AM (#15566994)
> 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 never used a logging framework that didn't allow you to configure where log statements were written, or give you control over the logging threshold for individual classes. However if your method is writing logs then presumably there is a reason, which is just as useful in the tests. If you mock it out then you're testing against different code to the one you will actually run against.

> If you start using a custom delegate solution, then your code is not consistent with everything else that uses DI.

Passing functions as arguments directly is 'DI', just without the need to configure that through an external container. Reducing the amount of interfaces (often with a single implementation) and external configuration makes navigating the code easier.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @06:37AM (#15567134)
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 by lkitching on Friday October 27, 2017 @07:16AM (#15567319)
> its about the fact you don't control an external dependency during testing

If your code is structured using small static functions, you don't have any dependencies in the first place, just arguments you are passed and transform. You will probably create interfaces for external services you depend on, but you can avoid needing to mock them if you express the transform directly.

> This is very assuming

I'm not assuming anything, since I wrote the static method and I also decided to call it, presumably for the result it calculates. Your argument appears to be that static methods could contain bad code but that applies to all code you depend on.

Re:Write tests. Not too many. Mostly integration by marcosdumay on Friday October 27, 2017 @11:33AM (#15569383)
You mean that the tests will depend on the thing being tested? What a crime!

> what if at some point later another developer adds a database call into the static method to do some logging?

Then you have a developer that does not grasp the idea of functions, and how they can help you improve your code. That's a call for education, not for changing your tests.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @11:42AM (#15569482)
The point is that tests are omnipresent, people aren't. I've worked at places where all sorts of dumb code has got through because there is no automation in place to stop it, and everyone else is too busy to do code reviews.
Re:Write tests. Not too many. Mostly integration by Jach on Friday October 27, 2017 @09:23AM (#15567995)
In Java, you can use PowerMock to mock or spy anything, even private static final things. I consider it a smell (though excessive mocking even without powermock is its own smell), but it's immensely valuable to get code you can't change (or fear changing because of its complexity and lack of tests, or simply don't have time to change because the refactoring would take a whole sprint) to have some tests.

You don't need interfaces for everything in order to do DI. Interfaces should be used only for having multiple implementations or to break dependency loops.

Other than that I'm in agreement, static methods generally aren't a good idea. They can all too easily balloon into big chunks of imperative code with inner dependencies (static or not) at 10 indentation levels deep. Non static methods can too, but not as easily, and you have more options for fixes/workarounds in those cases anyway. The only place they really make sense is as part of a set of pure primitive data transforms, and ought to be small.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @09:40AM (#15568117)
In C# we have Moq that can mock normal classes, though it requires adding 'virtual' to every method you want to override which is a code smell too. In Java everything being virtual by default I guess it doesn't matter. We like to always keep an interface around as it gets the developer used to working that way and keeps the code consistent. Visual Studio provides a quick shortcut to auto gen the interface too.
Re:Write tests. Not too many. Mostly integration by viraptor on Friday October 27, 2017 @08:51AM (#15567783)
> Now your testing will dirty whatever database you're using, as well as run 10x slower

It sounds like a problem is a few layers higher. Why is there a live database in your unit testing environment? Why are working credentials configured? If they're unit test, not integration tests, all db operations should be DId / mocked / whatever. Any call that isn't should fail, not take longer time. Db interaction is for the integration tests.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @09:07AM (#15567891)
That's exactly my point, mock your external dependencies. Static calls don't allow you to do that.
Re:Write tests. Not too many. Mostly integration by viraptor on Friday October 27, 2017 @05:11PM (#15572263)
In your language of choice:

    static_function(db, other, arguments) { ... }
    test { static_function(fake_db, 1, 2) }
You can even omit the db in the standard case if your language allows default keyword arguments. In almost every language, a method is just a fancy static call that takes extra arguments implicitly. (Closures are poor man's objects, objects are poor man's closures...)
Re:Write tests. Not too many. Mostly integration by vertex-four on Friday October 27, 2017 @09:42AM (#15568142)
So how exactly do you test that your SQL query does the right thing? That you're using the Twitter API correctly?
Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @10:02AM (#15568319)
Testing a database, or an external web service, is an integration test. They can be as simple as:

    void TestCreateUser() {
        var repo = new UsersRepository();
        var mockUser = new User("John", "Smith");
        repo.AddUser(mockUser); // db call
        var addedUser = repo.GetUsers().Single(); // db call
        Assert.StructureIsEqual(mockUser, addedUser);
    }
For the Twitter web service, you might test that you successfully get a response, as you don't have control of what exactly comes back.
Re:Write tests. Not too many. Mostly integration by quickthrower2 on Friday October 27, 2017 @06:31AM (#15567116)
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.
Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @06:41AM (#15567150)
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 by quickthrower2 on Friday October 27, 2017 @03:51PM (#15571649)
Because these tests become too detached from reality if you inject everything. A silly example to make the point:

IAdder { double Add(double a, double b) }

Test:

car mockAdder = ... // Mock 1+1=2 etc.

Re:Write tests. Not too many. Mostly integration by marcus_holmes on Friday October 27, 2017 @09:26AM (#15568018)
um... yes... this is actually what "pure" OO involves. The only reason we don't do this is because it's a nightmare to manage.
Re:Write tests. Not too many. Mostly integration by JustSomeNobody on Friday October 27, 2017 @10:41AM (#15568784)
“What if ...” doesn’t pass YAGNI.
Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @05:50AM (#15566960)
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 substitution and testing.

Re:Write tests. Not too many. Mostly integration by lkitching on Friday October 27, 2017 @06:43AM (#15567159)
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 by barrkel on Friday October 27, 2017 @05:22AM (#15566840)
TDD is completely orthogonal as to whether you write functional code that doesn't have a default receiver for routines, or OO code.
Re:Write tests. Not too many. Mostly integration by olavk on Friday October 27, 2017 @01:17PM (#15570349)
Yeah there is some cargo cult aversion towards statics.

Static methods with no side effect are wonderful, but static state is really bad and static methods which perform IO are horrible because they cannot be mocked in a unittest.

But some people miss this distinction and just say static methods are bad for testing.

Re:Write tests. Not too many. Mostly integration by knocte on Friday October 27, 2017 @04:39AM (#15566672)
C# is the new Java... facepalm
Re:Write tests. Not too many. Mostly integration by away2017throw on Friday October 27, 2017 @07:52AM (#15567495)
Rifle is the new pistol? You can shoot yourself with both?
Re:Write tests. Not too many. Mostly integration by Chris2048 on Friday October 27, 2017 @07:34AM (#15567403)
facepalm of enlightenment?
Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @04:53AM (#15566733)
The abstraction is consistent though, and familiarity is a good thing when navigating a codebase which has N amount of other devs pushing to it every day.

I practise TDD for peace of mind - if I add new functionality to existing code I can be 99.9% sure I haven't made any regressions. When a client's system goes down on a friday, I can 99.9% guarantee it wasn't my code that is at fault. If I have to work at the weekend to update a production server, I'm 99.9% sure it'll go smoothly as my tests say it will.

Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @05:33AM (#15566886)
Exactly this. Confidence is everything.

I can actually write entire features with appropriate test coverage from the ground up and they work first time and have close to zero defects in production.

It's amazing when you spend 5-6 days writing code that does nothing and at the last moment, everything slots together with a few integration tests and wham, feature done. Not talking trivial stuff here either; big integrations across several different providers/abstractions, bits of UI, the lot.

You see a lot of people arguing against this but I'm going to be honest, they churn out a lot of stuff that doesn't actually work.

Re:Write tests. Not too many. Mostly integration by gurkendoktor on Friday October 27, 2017 @03:34PM (#15571519)
> You see a lot of people arguing against this but I'm going to be honest, they churn out a lot of stuff that doesn't actually work.

My anecdata cancels out your anecdata. The TDD practitioners that I've met have, without exception, written code that worked fine for only the one case that they've tested. Example: They'd test a method for sending a message with the string "hello". Turns out the method didn't URL-encode the message before POST-ing it, and sending anything with a space was broken. They were confident and pushed the change.

Not saying you're wrong, just that TDD doesn't seem to work for everybody, and can even be a distraction.

Re:Write tests. Not too many. Mostly integration by cjsuk on Saturday October 28, 2017 @06:03AM (#15574594)
That will be because you worked with dumbasses.

If you only test the expected outcome you are a dumbass.

Re:Write tests. Not too many. Mostly integration by leftsidedhorn on Friday October 27, 2017 @05:49AM (#15566956)
How do you deal with http interactions in test? Do you mock it or maybe save it to disk to replay later?
Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @05:57AM (#15566996)
That's an integration test really. The clients all have an abstraction around the http endpoints so nothing touches integration in unit tests. The advantage of this is you deal with transfer objects only in the code, no HTTP which would violate separation of concerns.

I use HttpMock myself in test cases which fires up an http server for personal projects. We use Wiremock commercially.

Re:Write tests. Not too many. Mostly integration by mohaine on Friday October 27, 2017 @08:54AM (#15567802)
Here is how I handle it

1) Write a test that runs the service and saves output to a file. 2) Mock out the call to just return the data from the file and validate results. 3) If you need variations on this data just modify the file/data (often as part of the test)

I usually leave number 1 in the code but disabled since it often relies on remote data that may not be stable. Having the test run more than once is not very beneficial but being able to run it later and see what exactly has changed is great.

Re:Write tests. Not too many. Mostly integration by FanaHOVA on Friday October 27, 2017 @05:11AM (#15566804)
In this case, what's the difference if you write the test before or after though? You would still be covered. I don't lean in either directions in this argument, just curious to understand.
Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @05:39AM (#15566901)
If you write the tests afterwards, you find where all the tight coupling and crap abstractions you accidentally did are afterwards.

That leads to you either not writing tests or having to refactor bits of it. It's easier to do this much earlier on.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @05:18AM (#15566831)
The difference is night and day - writing tests first means you write 'testable code' from the beginning. Following the red, green, refactor mantra means that for every change to your code, you already have a failed test waiting to pass. The result is your test cases make a lot more sense and are of a superior quality.

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.

Re:Write tests. Not too many. Mostly integration by seanwilson on Friday October 27, 2017 @06:01AM (#15567010)
> 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 behaviour in my head for a good amount of time and find it's best to add comments at the "let's clean this up for production" phase when you can take a step back and see what needs commented. If you comment as you go, you'll have to update your comments as the code changes and sometimes throw comments out which is a waste of time.

Likewise with tests, I'm not saying write them far into the future, but I think having to strictly stick to red/green/refactor is going to waste time. What's wrong with writing a small chunk of code then several tests when you're mostly happy with it? Or writing several tests at once then the code?

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @06:26AM (#15567104)
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 when I didn't write it in a testable way to begin with.
Re:Write tests. Not too many. Mostly integration by seanwilson on Friday October 27, 2017 @06:48AM (#15567182)
> 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 by hacker_9 on Friday October 27, 2017 @07:20AM (#15567334)
What you describe is the typical mindset against TDD, it's difficult to explain the benefits, and really you just have to experience them for yourself. Changing your mindset is difficult, I know, why change what works right? My only tip is to keep an open mind about it, as TDD benefits are often not apparent to begin with, they only come after a couple of days work or weeks or months later or even years later.

You find that you need to do less mental work, as your tests make the required abstractions apparent for you. 'the minimum thing that makes the test pass' ends up being the complete solution, with full test coverage. Any refactoring done is safe from regressions, because of your comprehensive test suite. And when other colleagues inevitably break your code, you already have a test lying in wait to catch them in the act.

Re:Write tests. Not too many. Mostly integration by viraptor on Friday October 27, 2017 @08:37AM (#15567704)
> Any refactoring done is safe from regressions, because of your comprehensive test suite.

As much as I like the idea of TDD, I have a problem with this part. When some refactoring is needed, or the approach changes, it seems like you have two choices. One is to write the new version from scratch using TDD. This wastes extra time. The other is to refactor which breaks all the guarantees you got before. Since both the code and the tests are changing, you may lose the old coverage and gain extra functionality/bugs.

And unfortunately in my experience, the first version of the code rarely survives until the deployment.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @09:05AM (#15567884)
I'm not sure what approach you've described here, but it isn't TDD. In the case of adding new features to existing code, as you are continually running tests you will know straight away which you have broken. At this point you would fix them so you get all green again before continuing. In this way you incrementally modify the codebase. Remember unit tests are quite simple 'Arrange, Act, Assert' code pieces, so refactoring them is not a time sink.
Re:Write tests. Not too many. Mostly integration by sobani on Monday October 30, 2017 @01:12PM (#15586840)
refactoring != adding new features.

Also some refactorings are easier with tests, some are harder.

The kind @viraptor mentiones is the kind that spans more than one compoment. For example when you decide that a certain piece of logic was in the wrong place.

The kind of refactoring that becomes easier is when you don't need to change the (public) API of a component.

Take for example the bowling kata. If you want to support spares and strikes and you need extra bookkeeping, that's the easy kind of refactor where your tests will help you.

But if so far you have written your tests to support a single player and now you want to support two players who play frame by frame... Now you can throw away all the tests that affect more than the very first frame. (yes in the case of the bowling kata, you can design with multiple players in mind, but that's a lot harder in the real world when those requirements are not known yet)

Re:Write tests. Not too many. Mostly integration by seanwilson on Friday October 27, 2017 @01:32PM (#15570484)
> What you describe is the typical mindset against TDD, it's difficult to explain the benefits, and really you just have to experience them for yourself. Changing your mindset is difficult, I know, why change what works right? My only tip is to keep an open mind about it, as TDD benefits are often not apparent to begin with, they only come after a couple of days work or weeks or months later or even years later.

I've been forced to follow TDD for several years and also been given the same kind of comments to downplay any reasoned arguments against it which I find frustrating to be honest. I don't see why the benefits wouldn't be immediately apparent.

> You find that you need to do less mental work, as your tests make the required abstractions apparent for you. 'the minimum thing that makes the test pass' ends up being the complete solution, with full test coverage. Any refactoring done is safe from regressions, because of your comprehensive test suite. And when other colleagues inevitably break your code, you already have a test lying in wait to catch them in the act.

You can do all of the above by writing tests at the end and checking code coverage as well.

Re:Write tests. Not too many. Mostly integration by maxxxxx on Friday October 27, 2017 @09:07AM (#15567892)
"Any refactoring done is safe from regressions, because of your comprehensive test suite. "

With the right tests this works great. I have also seen the opposite where a test suite was extensive and tested the last details of the code. Then the refactor needed more time to figure out what the tests are doing than the actual refactoring. As often, moderation is the key to success.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @09:22AM (#15567986)
Unit tests should follow a simple 'Arrange, Act, Assert' structure and test one single thing, described in it's title. I agree anything too complicated starts to defeat the point, especially when we are mainly after a quick feedback loop.
Re:Write tests. Not too many. Mostly integration by dep_b on Saturday October 28, 2017 @12:54PM (#15575738)
How to implement an SDK that performs real-time manipulation of audio without really knowing how to describe the correct result using TDD?

Only when I understand the problem, the SDK and the expected result I could start writing some tests.

TDD seems to be for business applications where you're using tried and tested technology to implement new problems.

Re:Write tests. Not too many. Mostly integration by Chris2048 on Friday October 27, 2017 @07:38AM (#15567415)
> it's difficult to explain the benefits, and really you just have to experience them for yourself. Changing your mindset is difficult, I know, why change what works right? My only tip is to keep an open mind about it

Scientologists make the same argument

Re:Write tests. Not too many. Mostly integration by seanmcdirmid on Friday October 27, 2017 @08:56AM (#15567817)
Maybe writing code for exploration and production should be considered separate activities? The problem with these coding ideologies is that they assume there is only one type of programming, which is BS, the same as assuming a prototype is the same as a working product.
Re:Write tests. Not too many. Mostly integration by seanwilson on Friday October 27, 2017 @01:22PM (#15570407)
What's exploratory programming though? Unless you're writing something that's very similar to something you've written before and understand it well, most programming involves a lot of exploration.
Re:Write tests. Not too many. Mostly integration by seanmcdirmid on Friday October 27, 2017 @01:50PM (#15570650)
Well, UX prototypes for one. In research, most projects never go into production, those that do do so without researcher code. Heck, even in a product team, if you are taking lots of technology risks in a project, you are going to want to work those out before production (and it isn’t uncommon to can the project because they can’t be worked out).
Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @09:29AM (#15568045)
To be fair, TDD is quite good for exploratory programming, as it makes you think about the intent or your API up front.
Re:Write tests. Not too many. Mostly integration by seanmcdirmid on Friday October 27, 2017 @09:37AM (#15568099)
Not really. It makes you commit to an API upfront, this is the exact opposite of what exploratory programming should be (noncommittal, keep everything open).
Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @10:04AM (#15568341)
No with TDD you don't need to go in with a structure in mind, the structures arise as you write more tests and get a proper understanding of what components you'll require. Red, green, refactor - each refactor brings you closer to the final design.
Re:Write tests. Not too many. Mostly integration by meheleventyone on Friday October 27, 2017 @11:23AM (#15569277)
That's the mantra often quoted but it always makes me think of the famous Sudoku example from Ron Jeffries. Basically as a mantra it falls down if you don't understand the problem domain. It's popular because it works for the sort of simple plumbing that makes up a lot of programming work. This problem is particularly true for anything creative you're trying to express as the requirements are often extremely fuzzy and require a lot of iteration.

If you don't know how to solve a problem you actually need to do some research and possibly try a bunch of different approaches. Over encumbering yourself with specific production focused methodologies hurts. If you're doing something genuinely new this can be months of effort.

After the fact you should go back and rewrite the solution in a TDD manner if you think it benefits your specific context.

Re:Write tests. Not too many. Mostly integration by seanwilson on Friday October 27, 2017 @01:24PM (#15570419)
> After the fact you should go back and rewrite the solution in a TDD manner if you think it benefits your specific context.

Why? Why not add tests to your existing code?

Re:Write tests. Not too many. Mostly integration by seanmcdirmid on Friday October 27, 2017 @01:32PM (#15570486)
Because it was never meant for production at all.
Re:Write tests. Not too many. Mostly integration by seanmcdirmid on Friday October 27, 2017 @10:17AM (#15568504)
That really isn’t exploratory programming. The end result should be code that you throw away en masse (it should in no case reach production). Otherwise, production practices will seep in, you’ll become attached to your code and the design it represents, hindering progress on the real design.

When I was a UX prototyper, none of my code ever made it into production.

Re:Write tests. Not too many. Mostly integration by crdoconnor on Friday October 27, 2017 @08:17AM (#15567607)
>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.

That's why it's better to start with the highest level tests first and then move down an abstraction level once you have a clearer understanding of what abstractions you will need.

Re:Write tests. Not too many. Mostly integration by seanwilson on Friday October 27, 2017 @01:35PM (#15570513)
Can you do that with TDD though? Why not just sketch the code out first before you start writing tests?

I find TDD proponents don't take into account that writing tests can actually be really time consuming and challenging, and when you've got a lot of code that is tests, refactoring your tests becomes very tedious.

Re:Write tests. Not too many. Mostly integration by crdoconnor on Friday October 27, 2017 @06:12PM (#15572619)
You can do that with TDD (it's called outside-in), although I agree that it is time consuming and challenging, especially without the right tools.
Re:Write tests. Not too many. Mostly integration by konart on Friday October 27, 2017 @06:22AM (#15567091)
>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?

Define "all written". If we are talking about a new function - obviously you write you comment for it after the function ready to be commented on. And obviously you won't be commenting every string you put there, right?

Now, if we are talking about the whole new feature, that can consist of many functions and whatever - yeah, you usually comment your code in the process of writting the feature, rather than doing it at a later time, which will never come.

Re:Write tests. Not too many. Mostly integration by ryanmarsh on Friday October 27, 2017 @08:54AM (#15567808)
Comments / tests are a bad analogy. HN will over index on this and go down a rabbit hole.
Re:Write tests. Not too many. Mostly integration by markatkinson on Friday October 27, 2017 @05:33AM (#15566887)
I also find when following red, green, refactor that you end up producing more targeted unit tests that are more expressive of the code you are testing.

Trying to write unit tests afterwards lands me with something that appears as more of an afterthought or add on. It doesn't have to be this way I suppose, but it is more prone to.

This might be because I am more used to the red, green, refactor method though.

Re:Write tests. Not too many. Mostly integration by coconut_crab on Friday October 27, 2017 @10:53AM (#15568953)
I also practice TDD, but with a different 'T' - Type Driven Design. I find them much easier to reason about with types and safer (you can't compile your code if it doesn't pass the type check). Just model your data as ADT and pattern matching accordingly.

Of course, types alone can't represent every error cases out there (especially the one related to number or string), so I still write Unit Test for those cases. But the number of Unit Tests needed is much lower.

Re:Write tests. Not too many. Mostly integration by marcosdumay on Friday October 27, 2017 @11:36AM (#15569416)
That would be ATDD - Abstract Types Driven Design.

It completely fails on concrete types. That's the reason haskellers create a DSL for everything.

Re:Write tests. Not too many. Mostly integration by pjmlp on Friday October 27, 2017 @05:35AM (#15566891)
Is GUI code that 0.1%?

Because I am always keen to understand how to TDD GUI code and I don't mean the data model behind the pixels.

Re:Write tests. Not too many. Mostly integration by hacker_9 on Friday October 27, 2017 @05:42AM (#15566923)
Visual tests are more general, and are more akin to putting up barriers on either side of a bowling lane so the bowling ball stays within it's lane (with room to move about still). For example when using Angular, you write 'Page Objects' that have methods such as .getTitle(), .clickListItem(3) and so on, and can then write assertions to make sure the UI changes as expected by inspecting properties [1].

I usually find I build a general page object first ('this text is somewhere on the page'), then write the UI, then make the test more specific if I can after (but it's an art, as too specific and you risk creating too many false negatives when you make UI changes).

(Also as you are interacting with the UI, these would be known as integration tests.)

[1] https://semaphoreci.com/community/tutorials/using-page-objec...

Re:Write tests. Not too many. Mostly integration by pault on Friday October 27, 2017 @09:02AM (#15567869)
I don't think you can unit test GUIs, since by their nature all tests end up being integration tests. It's easy if you assign non-css (i.e. use a data-* attribute for identification instead of id or class since you want to keep those variable for stylesheet refactors) identifiers and just hard code the assumptions into the tests, like "when x is clicked y should be visible", or "when I enter 'foo' into the text field, the preview label should contain 'foo'". Ideally your assumptions about GUI functionality shouldn't change much throughout the lifetime of the project, and if you use static identifiers your tests should hold up during extensive refactoring.
Re:Write tests. Not too many. Mostly integration by pjmlp on Friday October 27, 2017 @09:37AM (#15568095)
That is my point of view, since my focus are mostly native GUIs.

Which is why it is my main question on TDD talks, that always use some library or CLI app as example.

Re:Write tests. Not too many. Mostly integration by maxxxxx on Friday October 27, 2017 @09:09AM (#15567909)
To a certain degree you can unit test GUIs with tools like Ranorex or Selenium. The question is how much setup you need to get the GUI on the screen with the right data.
Re:Write tests. Not too many. Mostly integration by pault on Friday October 27, 2017 @09:42AM (#15568141)
That isn't unit testing though, it's integration or e2e testing.
Re:Write tests. Not too many. Mostly integration by pjmlp on Friday October 27, 2017 @09:38AM (#15568103)
You can, but usually with lots of effort and cannot test UX and design requirements anyway, which is why I tend to make this question about full TDD based processes.
Re:Write tests. Not too many. Mostly integration by cjsuk on Friday October 27, 2017 @05:46AM (#15566941)
We use Ranorex for this: https://www.ranorex.com/
Re:Write tests. Not too many. Mostly integration by pjmlp on Friday October 27, 2017 @07:15AM (#15567314)
Interesting, thanks for the link.
Re:Write tests. Not too many. Mostly integration by TeMPOraL on Friday October 27, 2017 @06:13AM (#15567057)
I think so. I don't know how to unit test GUIs either.
Re:Write tests. Not too many. Mostly integration by oddlyaromatic on Friday October 27, 2017 @06:56AM (#15567212)
I saw an enjoyable talk recently about snapshot testing. I don't know too much about testing generally but it seems like it could be relevant: https://facebook.github.io/jest/docs/en/snapshot-testing.htm... is the general idea but it doesn't have to be confined to jest/react

Edit: Slides from the talk I saw: http://slides.com/bahmutov/snapshot-testing

Re:Write tests. Not too many. Mostly integration by TeMPOraL on Friday October 27, 2017 @11:37AM (#15569419)
Past my edit window, but I want to add this - I feel that I introduced some confusion by missing one magic word in one special place. The first sentence of the last paragraph should be:

That said, if you go for functional style in OOP, i.e. shoving as much as you can into stateless static helper functions and most of the rest into dumb private stateless functions, (...)

Of course I do not mean you should abandon objects where there is a strong connection between a set of data items and operations that work on them, or where polymorphism is a right abstraction. But from my experience, quite a lot of code is made through transformations applied on simple data, and when you write that kind of code in a functional style (whether as static methods grouped in helper classes, or private methods within an implementation of your class), both quality and testability rises in lockstep. And my point is that quite a lot of code can be written this way even in an OOP project.

Re:Write tests. Not too many. Mostly integration by ivanche on Friday October 27, 2017 @06:28AM (#15567109)
>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 in OOP! For more details I recommend excellent Misko Hevery's article "Static Methods are Death to Testability" [1]. Also, I'd argue that "functional style in OOP" is an oxymoron - you're either OO or something else (functional, imperative...)

[1] http://misko.hevery.com/2008/12/15/static-methods-are-death-...

Re:Write tests. Not too many. Mostly integration by majewsky on Friday October 27, 2017 @06:46AM (#15567171)
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 by andrewaylett on Friday October 27, 2017 @08:17AM (#15567608)
The OOP = Java trap is all too common, but the converse is also a trap: just because you've written OOP code in a different environment doesn't mean that pattern will work in Java.

Go with what the ecosystem supports, and you'll find your tooling helps you a lot more than if you fight against it by trying to force non-idiomatic structures. Your colleagues will appreciate it, too.

Re:Write tests. Not too many. Mostly integration by profalseidol on Friday October 27, 2017 @09:02AM (#15567863)
He probably meant no-side-effect static functions. I myself find using these a lot. For common CRUD web apps, you have Spring doing most of the stuff for you and you simply need to write stateless methods. However, for not-common requirements, you might need to use classes and OOP patterns to implement a complex logic.
Re:Write tests. Not too many. Mostly integration by TeMPOraL on Friday October 27, 2017 @11:31AM (#15569352)
I did. I even wrote the magic word a moment later, but forgot it there, and realized only past the edit window.
Re:Write tests. Not too many. Mostly integration by bpicolo on Friday October 27, 2017 @09:13AM (#15567936)
It seems the encouraged method is IoC these days, and that's just dreadful. IoC/Dependency resolution all over make it insanely hard to reason about code without running circles through the codebase.

For me, IoC seems invented almost entirely to make up for how difficult testing can be in particular languages. Which, sure, making up for shortcomings as good, but the necessity to use IoC for it feels bad.

Re:Write tests. Not too many. Mostly integration by GordonS on Friday October 27, 2017 @10:50AM (#15568912)
This is how I felt about IoC as a junior dev 15 years ago, before I'd actually used it myself.

Nowadays the benefits are clear to me: more modular, testable code, and also lifecycle management.

Give it a try, you may well change your views.

Re:Write tests. Not too many. Mostly integration by bpicolo on Friday October 27, 2017 @12:30PM (#15569904)
I've used it a fair deal. I've found I prefer languages that don't require IoC to make code testable.

I agree that it's one of the sanest options when it's required, I just think that language design should incorporate testing ergonomics from the start.

Re:Write tests. Not too many. Mostly integration by mpweiher on Friday October 27, 2017 @05:51AM (#15566967)
> unnecessary abstraction and boilerplate patterns

That means you didn't actually change the code. It means you added unnecessary abstractions around your code in order not to change it.

Unit tests guide you towards simplicity. In my experience, the only times they haven't done that is when I have made some assumptions about what the code should be and not allowed the tests to drive me towards that simplicity.

http://blog.metaobject.com/2014/05/why-i-don-mock.html

Re:Write tests. Not too many. Mostly integration by al2o3cr on Friday October 27, 2017 @07:12AM (#15567287)

    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,

We had this at a company I worked at a while back - dozens of modules with nothing but static functions that all took a first argument of the same type. If only there was some kind of METHOD for declaring a whole bunch of functions that operated on the same data...
Re:Write tests. Not too many. Mostly integration by meheleventyone on Friday October 27, 2017 @11:38AM (#15569433)
Until you get into polymorphism etc. this is just a style thing.

method(a,b) is equivalent to a.method(b) and exactly as much typing. You do save manually typing the extra part of the definition but 'eh'. A few languages treat these interchangeably.