1. 13

  2. 11

    As usual, this misses the one important point of unit tests in languages with few static guarantees: To test that various code paths actually work at all. How to be sure that you don’t get a type mismatch exception in some error case except by faking that error?

    For languages like Go, I tend to agree with the author.

    1. 4

      How to be sure that you don’t get a type mismatch exception in some error case except by faking that error?

      Not to be that guy, but… static type systems sort of get rid of that whole class of issues without the need for tests.

      Writing big mission-critical systems in languages where a single typo or a function signature change can bring down the production system at some random point in the future seems unwise, the kind of unit testing which mostly exists to catch type errors and typos seems like a pretty expensive fix.

      1. 3

        Same point of view. Try ditching all unit tests on a PHP codebase, or a JS one. A strong type system really shines, and often outclass unit tests.

      2. 11

        Unit tests are intended to speed up development. If they do the opposite, then yes, you should stop writing them, because they’re not useful to you.

        Two ways in which unit tests do speed up development:

        • When tests fail, unit tests help pinpoint the part of the code that needs to be fixed.
        • Integration tests often take longer to execute than unit tests, so a good set of unit tests can speed up the change-compile-test cycle.

        The author hit the nail on the head here:

        When a large change lands, unit tests, in the best case, need to be heavily modified, and in the worst case are obsolete and need to be removed.

        In a development cycle involving unit tests, we do our best to avoid large changes. If a given change requires changing lots of tests, then either the change itself is too large, or the tests are not really unit tests.

        1. 2

          Well put. This was also going through my head while reading the article. “Unit” and “integration” tests serve different purposes.

        2. 6

          I couldn’t disagree more with this, especially on the dismissal of mocks. I write a lot of c# using interfaces in the standard fashion and the amount of times that I’ve seen code break that just wouldn’t have happened if the developer had written unit tests. It forces the developer to think defensively about what your inputs can be, especially with api’s where you can’t assume that you’re going to get happy path input.

          Might be good advice for other languages/patterns but would be bad advice for the dotnet world anyway.

          1. 2

            Mocks can also be incredibly useful in the real-world case where you inherit a large, legacy codebase with poor test coverage around external integrations and need to make changes to this code. (Think about inheriting a 100KLOC+ Rails codebase for example). I realize there are many good arguments against using mocks, particularly when mocking your own code, but there are also situations where they are simply the best option available.

            1. 2

              Mocks also let tests run faster than actually hitting the filesystem/database/api. Speaking of api, what if I want to test against the web teams api that they are delivering in a month? Write a mock, then validate my test against the real api to integrate once it is available.

          2. 4

            Care to report your test coverage (preferable branch coverage) before and after?

            And then be fair in your reporting and assuming an industry typical number of bugs per untested branch…..

            ….care to estimate how many bugs you have left, as yet undiscovered by your integration testing, awaiting some poor adventurous user that wanders off your happy path?

            1. 4

              The first is that a unit test without mocks is really an integration test, because it only tests the “outs” for the given “ins”, and thus is a good thing to write.

              This is redefining “unit” and “integration” in a way nobody uses them to support his point. Under this assert 1 + 1 == 2 is an integration test.

              A much more useful definition, I’ve found, is that integration tests involve system emergence, while unit tests do not. This explains a lot of the differences between the two, as well as why people tend to prefer one or the other.

              1. 3

                While unit vs integration testing may be debate over a definition of a word, I really disagree with author’s point about mocking. Some see mocks as merely a way to avoid wasting resources on instantiating a real thing. A lot of time, however, we work with code that may be and often is flawed. Testing against a mock and then testing the same code with the real thing allows us to tell whether our code is not working right according to its own beliefs, or the real thing does not live up to the functional requirement it promised.

              2. 3

                I was about to look up The Three Rules of TDD when I hit this gem: https://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html . Specifically:

                His tests are tightly coupled to his production code.

                These days, I don’t make much of a distinction between unit and integration tests. Staying focused on the minimal amount of code to test a feature, and keeping an eye on the design, usually leads me to just the right amount of code.

                1. 2

                  Wow, thanks for sharing that Uncle Bob post. I wish I had read this sooner in my career.