Live data from Hacker News

Giving up on test-first development

iansommerville.com

131–140 of 224 posts

Re: Giving up on test-first development

#131
post #65

Earlier quoted context omitted.

I'd say do both. Unit tests often help me to make my code more readable/decoupled and also help to spot potential problems early on. They also act as a kind of abstract documentation for how things are supposed to behave/work. But functional/integration tests are what matters most in terms of being confident of deploying big changes because you can ensure that all the endpoints that are actually consumed by clients w…

For smaller projects I definitely agree with having both unit and integration tests, especially for libraries. One thing to look out for is the fact that you can always "cheat" in unit tests, e.g. you can be "lazy" and set up some internal state directly in the test to skip huge amounts of initialization, this of course becomes a problem when there's no actual integration test for making sure that the exact state can…

yeah, well you just test what you own anyway, for other stuff you could just grab some kind of dummy response and use that for testing your manipulation of that data, but of course you have to trust your customers endpoints to return the data in the correct format because that is out of your control.

Re: Giving up on test-first development

#132

> ... it encourages you to make design decisions that can be tested rather than the right decisions for the program users... This is exactly why I don't do TDD. I prefer to think about the design and implement features in non-distracting way. After I have it shaped, I add tests to avoid regressions. Where I do write tests first is when I need to reproduce a bug, so I have the test, again, to prevent regressions.

I'm in the same boat, but I add tests almost always not too long after implementing - I think the more important thing is to write the tests.

Re: Giving up on test-first development

#133
post #5

From the article: Because you want to ensure that you always pass the majority of tests, you tend to think about this when you change and extend the program. You therefore are more reluctant to make large-scale changes that will lead to the failure of lots of tests. Psychologically, you become conservative to avoid breaking lots of tests. Interesting. I've often found that the lack of tests leaves me absolutely terri…

> I've often found that the lack of tests leaves me absolutely terrified of making changes to large Ruby and JavaScript applications.

I agree. If I'm working on some part of the app that doesn't have any test coverage, I might add some high level tests before I start.

Re: Giving up on test-first development

#134
I'd describe myself as a "TDD guy." Probably the type that most of the TDD-negative comments are about. I've got quite a bit of experience doing TDD in large applications, mostly web or services for consumption by a web-app and mostly Ruby and JavaScript (a bit of Java as well). Here are some of my thoughts.

- The best test that you can write is a complete system integration test. These are usually driving a browser and integrating with a database for web apps, or making HTTP requests if it's an API / headless service. They're the best because they guarantee that the system works as a whole given whatever initial setup you do.

You can totally write these tests first. And you should. It requires you to stop and think about how the system should behave at the boundaries (interaction with the user and external systems such as a database or API). Then as you start writing the code, you don't have to do a bunch of clicking in the browser, you run a test that takes 2 seconds to know if your thing worked

- The best test that you can write is also the slowest. Got some complicated flow that behaves differently in 10 different contexts? Full end-to-end tests are too slow for this. 6 months of writing tests like this and you're looking a 10+ minute test suite, _at best_.

- If the best test you can write is also the slowest, you need more tests somewhere. If you don't have those tests somewhere, then code you write today is going to break at some point and you won't know until it hits production. This sucks. This is where unit tests come in.

- Unit tests should test exactly that, the unit. That means, the behavior of one class or function. The behavior of an object I depend on is _not_ my behavior. Therefore, if I have dependencies, you should be mocking them out. It's true that this in a way tests implementation, not behavior. But thought about another way, the behavior of one unit might be to call a method on one object and pass the result to another.

Thinking about unit tests in this way and mocking collaborators prevents the issue where you make on change and break a _ton_ of tests. It also prevents you from creating a bunch of duplicate setup when your code is broken up into lots of small objects / functions. If you don't mock your dependencies, you're setting up data for something that isn't relevant until multiple levels down the dependency chain and it's not obvious why that setup is necessary.

- Sometimes things are just too complicated and mocking all the collaborators of an object just isn't worth it. These situations should be pushed as far toward to bottom of your abstraction hierarchy as possible and then you should do an integration test from that class / function down to the bottom with no mocking.

- TDD isn't the only way to have great test coverage or the only way to write well-decoupled components, but it's hard to have bad test coverage and write highly coupled components when test-driving correctly. And when you're a year or more into building an application that's a cornerstone of your business, it's going to be super valuable to have the flexibility of a well-tested, loosely-coupled application. You won't have to spend hours in manual testing to confirm that you haven't broken anything and changes will be easier to implement.

Re: Giving up on test-first development

#135
post #58

When writing tests is boring, difficult, and tedious, that's a really good time to think hard about the way the program is structured, if you have time for this. The way to make testing pleasant is to extract more and more behavior into units with clear boundaries... realizing how to do this was a major event in my programming career, and I attribute the insight partly to doing TDD. I don't agree with some posters wh…

It encourages dependency injection, which in my experience will eventually encourage a functional programming style since there's so little barrier to it once you're doing things like injecting the current time into a method which makes use of it. Maybe I'll turn around in five year's time and regret saying this, but I've never regretted pushing a project in a functional direction.

Surely the value of functional style depends on language. Some languages have better guarantees that support functional styles.

Re: Giving up on test-first development

#136
Many people here are missing the point. This is not about not writing tests at all*, just not to focus on them first.

I usually write tests first when I already know what I want, or when testing manually is really time consuming. Otherwise I don't care when it happens as long as they are there before pushing to remote.

Re: Giving up on test-first development

#137

C.S. Degree in 1996, 20 years "professional" programmer and I never once thought TDD was helping my project. Every time I did it, it was because the boss told me I had to. Litmus test: every side project I did just for me, I never did TDD.

How many of those side projects ended up being 6 million lines of code maintained over 5 years? Because those are the kind of code bases I have in mind when I'm weigthing the pros and cons of TDD or other testing practices.

When reading Uncle Bob and others I have always got the feeling that the "goal" of the practices described is to have systems that can be maintained and extended for years by different people and teams. It never crossed my mind that Uncle Bob would recommend TDD for that tic-tac-toe I wrote to learn Buzzscript.

Re: Giving up on test-first development

#139
post #42

Earlier quoted context omitted.

Testable design is often a poor design, mainly because of language limitations; abstraction and scoping for tests are not necessarily the best choice for abstraction and scoping for design.

Can you give an example? I'm struggling to think of a language where that can't be worked around without bending the original code.

  public class Foo {
    private static class Bar {
        public void someMethod() {
      }
    }
  }
Unit test someMethod.

More realistic, concrete examples are harder to describe because they involve an interaction over time between a somewhat vague problem description and exploration of a solution state space, where the abstractions chosen are fluid and slide around before they get into a good shape.

I, for one, tend to write code from the bottom up, i.e. creating hypothetical abstractions, small tools etc. and start composing them to solve the next problem up the abstraction stack. Then, when I find it doesn't fit quite right, I adjust, freely throwing away abstractions, rewriting them, reshaping, until the level of my tools fits the problem better. I gradually build up my abstraction level until I can move problem domain level mountains with little effort. Doing this in the form of tests just doesn't work (for me).

Writing code from the top down isn't the right answer either; not all people think that way, and besides, it can lead to solutions with very ugly implementations - the grain of the wood should influence the design of the house, if you will.

Re: Giving up on test-first development

#140
post #96

I recall a situation where a manager was pushing for TFD in an Agile environment. What a nightmare, the two just don't mix. The biggest issue isn't the approach itself, but when it hits Buzzword level with the managers who have no idea how development really works.

This is a weird argument, since both (Agile & TDD) were codified by basically the same people, or at least two sets of people with a big old overlap.

It's a way of offloading more work onto the coders so that the PMs can just go down a checklist instead of having to actually manage. Now instead of just constantly refactoring, you need to write new tests each time first. Then it's sold to us as making us "better coders." It doesn't make us better coders. It makes us easier to manage and puts even more work on our plates.
Post reply on HN