News for hackers. Stuff that matters.
Re:Write tests. Not too many. Mostly integration by josteink on Friday October 27, 2017 @01:54AM (#15566023)
I work in a company where quite a few of the developers simply are incapable of writing anything but integration-tests.

The reason? They don’t “believe” in unit-tests. They don’t think unit-testing “works in the real world”.

They absolutely fail to accept that they need to write their code differently for automated testing to work well.

How do you change such a mindset?

Re:Write tests. Not too many. Mostly integration by spc476 on Friday October 27, 2017 @03:18AM (#15566326)
For some reason, unit tests vs. integration tests reminds me of this image: https://pbs.twimg.com/media/CZX0O-tWQAAeaLi.jpg The unit tests past, but why bother with integration tests?

I wrote a component at work. Sure, there are some unit tests (the SIP parser working? Check. But for that component, unit tests only go so far as I need to query another program that actually implements the business logic (it goes SIP -> my program -> custom protocol [1] -> business logic program and back again). To mock the business logic program (I need to make sure to return the proper information per the SIP request) is to reimplement the business logic program, so when testing my component, we also use the business logic unit. The business logic also requires two more programs to run. At this point, there is no difference between a unit test and an integration test as I'm running five programs ... no, ... six, I do have to mock a cell phone (basically, respond to a custom protocol and make a web request), to test the "unit" that is my program.

Oh, and to make it even nicer, this is legacy C and C++ (C with classes) code.

[1] Legacy code. It works. At the rate of production deployments we (our team) gets, it would be around two years [2] to remove the custom protocol. So it stays.

[2] Have I mentioned the very scary SLAs?

Re:Write tests. Not too many. Mostly integration by grey-area on Friday October 27, 2017 @03:38AM (#15566416)
Assuming you are a developer, start writing some.

Next bug you find that needs a unit/functional test (e.g. it is caused by a simple error in transformation in one function), write the test first as a table of inputs vs outputs, find it fails, fix the function, and leave the test in. Gradually, the code base will contain unit tests which are useful, people will see they are useful, and other people might start using them too where appropriate.

You are unlikely to persuade them without actually doing what you say is beneficial and exposing others to its benefits.

Re:Write tests. Not too many. Mostly integration by webignition on Friday October 27, 2017 @04:36AM (#15566653)
I agree. Tests for bug fixes are extremely valuable. Of such tests, unit tests are often very feasible.

A test accompanying a bug fix holds value in many ways.

Firstly, it demonstrates to those reviewing the change that the fix is suitable.

Secondly, the presence of a test encourages reviewers to consider what a test does and doesn't cover, sometimes resulting in comments regarding improvements that had not otherwise been considered.

Thirdly, and of most importance in the long term, a test for a bug fix serves to document oddities previously discovered that were for a time not known about.

Re:Write tests. Not too many. Mostly integration by mercutio2 on Friday October 27, 2017 @10:51AM (#15568921)
I’m confident you know this, but just for the peanut gallery:

Tests that go along with bug fixes are some of the highest value tests, but they must be previously failing tests.

I can’t tell you how many times I’ve reviewed fixes with “tests” that are basically affirming the consequent; they assert something that was already true and it turns out they’re not actually fixing the reported bug.

Re:Write tests. Not too many. Mostly integration by arkh on Friday October 27, 2017 @03:23AM (#15566353)
It depends on what you unit test and why.

If it's for 100% test coverage: forget about it.

If you test private methods: you're doing something wrong.

What people usually see from the "unit test evangelists" are codebase for which you have tests for every method in the code. Then you do some refactoring and you have to rewrite tons of tests. And as those tests are just made to get 100% coverage you end-up with logic bugs because most unit tests have been written to go through the code, not check limits and edge-case. When you stumble upon this kind of test harness you can only think this as only cons (more to write upfront, less willingness to refactor) and no pro (the code is still brittle). Then your integration tests feel like your real harness: you can change anything in your code it'll tell you what has been broken when used.

Now if you consider your unit tests as a kind of integration tests for the API your classes present then you get the benefits of unit tests. But this mean testing only public methods. And mutation testing resilience is a better metric than test coverage.

Also: those tests do not replace a real documentation which can be a lot faster to read and understand than code.

Re:Write tests. Not too many. Mostly integration by BlackFly on Friday October 27, 2017 @05:35AM (#15566893)
People test private methods because edge cases occur in those private methods and the test for the edge cases do not belong in the unit test for the consumer of the private unit. If the consumer simply loops over a list of objects which it receives from the private unit, the consumer does not need to know that particular integer arguments are special cases in the private unit; that would be a leaky abstraction. However, it still makes sense to verify that you have correctly handled each special case via a unit test.

As for the difficulty or refactoring, if you refactor the private unit, you ensure that its tests continue to pass, since its consumers depend on that behavior: you ignore the failing tests of the consumers so long as the subordinate unit's tests are failing. If you eliminate the private unit, you eliminate its tests. Modifying the behavior of the private unit may be equivalent to eliminating the unit or refactoring it. The number of tests you will have to modify is equal to the number of units you modified the behavior of: the branch count of the private unit, or that plus its consumers. If each private consumer was responsible for testing all of the edge cases of the private unit, then you will have to change its branch count multiplied by the number of consumers worth of tests instead.

The distinction between private and public is wholly synthetic. It is a binary layering mechanism that does not map well onto most architectures which have many layers. From the perspective of many full architectures, everything down in the data layer is private: no customer will have direct access to the data layer. Yet you will still test the data layer.

The internals of a library are not special simply because the layering is thinner and binary.

Re:Write tests. Not too many. Mostly integration by andrewaylett on Friday October 27, 2017 @08:25AM (#15567642)
My general theory is that if a private method is complex enough to need separate testing, it's usually complex enough to pull out into its own class and test as a separate public interface. That's 'interface' as in 'what a class exposes to its callers', not necessarily using an actual Java interface or making it part of the public API of the library.

A side-benefit is that the tests for the original class can be a lot simpler too, as I can just mock the responses from what used to be the class internals. Another benefit for libraries, is that it allows consumers to swap out your implementation. I've lost track of the times I've wanted to change something deep inside a library but it's implemented in a private method so I can't even override it.

This does lead to more, smaller, class files. But unless taken to extremes I've not found it to make things less comprehensible, and it definitely makes things more composable.

Re:Write tests. Not too many. Mostly integration by growse on Friday October 27, 2017 @04:08AM (#15566541)
> If you test private methods: you're doing something wrong.

Maybe a silly question, but why?

If I refactor a class to pull a common piece of functionality into a private method, why would I not want a test for that?

One of the principle benefits of tests I see is allowing me to change the implementation without worrying about the behaviour, and I'm not sure why that wouldn't apply to private methods?

Re:Write tests. Not too many. Mostly integration by chewbacha on Friday October 27, 2017 @04:35AM (#15566647)
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 you test the public interface, generally refactors won’t break the tests.

Testing private functions also seems to be a smell that the overall setup of testing the class or function is too difficult. This can be because the class has too many branches in it, the argument list is too large, or too many other systems must be in place for it to function correctly. This, to me, indicates a public interface that is hard to use and will pass much of these issues on to the caller.

Lastly, if you are testing private functions to gain coverage then arguably the behavior in the private method isn’t actually useful to the public interface. The reason I say this is that testing the behavior of the class should end up touching all branch conditions inside the class or the public interface isn’t fully tested. By only testing the public interface it then also becomes easier to locate dead/unreachable code.

Hope that answers the why.

Re:Write tests. Not too many. Mostly integration by BlackFly on Friday October 27, 2017 @06:07AM (#15567030)
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 testing those edge cases is often critical to verifying that the unit adheres to its specified behavior under all conditions.

As for the smell, sometimes things are irreducibly complex. Some things in this world do require tedious book keeping. All the refactoring in the world cannot change the degrees of freedom of some problems.

Tests on consumers should not test branches of subordinate units. If you did this then the number of tests would explode exponentially with the number of branch conditions to handle all the corner cases. If a private unit produces a list of objects, but has special cases for some values of its argument, test those branches to verify it always produces the correct list. Then just make sure each caller does the correct thing with the list of objects. That is the purpose of separation of concerns: the consumer does not need to know that some values were special.

Re:Write tests. Not too many. Mostly integration by afarrell on Friday October 27, 2017 @06:49AM (#15567185)
> 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 by alien_at_work on Friday October 27, 2017 @01:48PM (#15570637)
I'm trying to imagine what on earth your private methods can be doing that wouldn't be affected by the public interface.

There should be no situation where the same exact call in the public interface could take multiple different paths in the private method. The only thing I can think of that could make that happen would be some dependancy, which should be mocked at the top level to control these cases.

Re:Write tests. Not too many. Mostly integration by hinkley on Friday October 27, 2017 @12:56PM (#15570144)
Some people call this functional testing.
Re:Write tests. Not too many. Mostly integration by arkh on Friday October 27, 2017 @04:17AM (#15566578)
Private methods are the internals of your classes. It may change a lot for performance or to make it easy to maintain, one method may become 3 or 4.

But people who use your class don't care. They input something in your public methods and expect something in return. The details of what happen inside should not matter. Adding tests there only help to slow you down and make the dev team resist needed changes. And when you add tests you increase the chances you make them useless or wrong.

Re:Write tests. Not too many. Mostly integration by growse on Friday October 27, 2017 @04:22AM (#15566590)
Ah, I think I see. If I break the functionality by changing the implementation of a private class, that should be reflected in the public API unit tests.
Re:Write tests. Not too many. Mostly integration by Marazan on Friday October 27, 2017 @04:34AM (#15566644)
That's how I see it (in Java at least), unit tests are for guaranteeing that your classes API does what it says it does.

In Python I am more loosely goosey about my unit tests and unit tests there are more for helping me write/think about tricky code.

Re:Write tests. Not too many. Mostly integration by weavie on Friday October 27, 2017 @04:31AM (#15566622)
If your private method is wrong, then your public methods will also be wrong. If your public methods are right, then it doesn't really matter what your private methods do..
Re:Write tests. Not too many. Mostly integration by josteink on Friday October 27, 2017 @04:12AM (#15566557)
I read it as using introspection/reflection to test what is essentially implementation details which are very likely to change.

This is how you write brittle tests which fail easily and cause high maintenance costs and reduced confidence in the unit-tests as a safety net.

Definitely an anti-pattern.

Re:Write tests. Not too many. Mostly integration by josteink on Friday October 27, 2017 @03:56AM (#15566491)
> If it's for 100% test coverage: forget about it.

Im a realist and don’t see any point or value in chasing that goal for a 20+ year old company code-base.

But I expect new modules to be fundamentally testable.

> if you test private methods: you're doing something wrong.

Agreed.

> What people usually see from the "unit test evangelists" are codebase for which you have tests for every method in the code.

Is that really so? It’s easy to be opposed to extremists of any form.

I only “evangelize” that business-logic should be tested and thus needs a code structure to isolate the business-logic from its dependencies (databases, services, factories, etc).

I find that perfectly reasonable.

Re:Write tests. Not too many. Mostly integration by mikegerwitz on Friday October 27, 2017 @09:08AM (#15567896)
> Then you do some refactoring and you have to rewrite tons of tests.

One of the core principles of TDD is that you write tests to facilitate refactoring---to make it _easy_ to refactor and have confidence in the system after doing so. I've been practicing TDD for ~8y and this is rarely a problem. TDD encourages good abstractions and architecture that lends itself well to composition and separation of concerns.

But if you change an implementation, of course a test is going to fail---you broke the code. It works exactly as designed. What you want to do is change the test first to reflect the new, desired implementation. Any tests that fail that you didn't expect to fail may represent a bug in your implementation.

Of course, I haven't seen the code, so I can't comment on it, and I won't try to do so.

Re:Write tests. Not too many. Mostly integration by discreteevent on Friday October 27, 2017 @11:32AM (#15569372)
"What you want to do is change the test first to reflect the new, desired implementation". Not sure if you meant this but this is exactly what is wrong with most unit tests that I have come across. They test the implementation and not the interface.

That's why I agree that the focus should mainly be on integration tests. Or at least functional tests. Ideally what you want is to have a system where all the state and logic is in a model (that model can include an external db). The gui should be as much as possible a function of the model i.e. model-view. Then you write the majority of your tests as integration tests against the model and include as many scenarios as you can think of. These tests should reflect the requirements/interface for the system and not the implementation. You should write some gui tests but these should be much less. They just need to verify that the ui reflects the model accurately. You shouldn't be testing scenarios as part of the gui tests.

I have come across too many code bases where the unit tests test that the code is what it is, rather than the code does what it should. Where 'what it should' == 'requirements/interface' == 'something close to integration tests'

Re:Write tests. Not too many. Mostly integration by hvidgaard on Friday October 27, 2017 @02:07AM (#15566088)
I doubt that you can. There was a study a while back, and I apologize in advance because I do not have a link, that showed projects written with unittests took significantly longer to reach the market, but with significantly less bugs. However, overall time spend on the code was less. So conclusion was that unittests are a commitment to a long term goal of minimizing developer time, and the tradeoff is that it takes longer for the first version to be done.

That is, as far as I know, the only tangible evidence that unittests are good unless you need to get something out the door quickly (which sadly is most of it).

I'd argue that is not the main benefit of unittesting however. That is the way code is structured, and especially how dependencies are explicit, e.g. injected with constructor arguments.

Re:Write tests. Not too many. Mostly integration by hinkley on Friday October 27, 2017 @01:00PM (#15570177)
That parallels my experiences. I got tired really early on with projects that ground to a halt because of brittleness and a lot of my focus is on building skill and confidence so that version 3 is no harder to ship than version 2 was. Every team I’ve left on good terms was more effective when I left than when I got there. The ones that fought me the whole way frustrate me and it shows.
Re:Write tests. Not too many. Mostly integration by chewbacha on Friday October 27, 2017 @04:39AM (#15566667)
Man, I really want to read that study!
Re:Write tests. Not too many. Mostly integration by alien_at_work on Friday October 27, 2017 @01:51PM (#15570657)
Was this the microsoft study? I think it was a breakdown of their delivery of Vista?
Re:Write tests. Not too many. Mostly integration by flukus on Friday October 27, 2017 @02:48AM (#15566237)
> How do you change such a mindset?

IME you can't. If they even recognize that what they are doing is not unit testing then your doing well.

> They absolutely fail to accept that they need to write their code differently for automated testing to work well.

I've been thinking lately that having to code differently might be a fault of the tooling that's built up over the years and that they might be right. I've been getting back into c lately and had a look at the mock/stub options there which were very complicated and not very compelling compared to what I've been used to in the .net world. In the end I found the pre-processor was the best (for my project) option:

#ifdef test #define some_method mock_some_method #endif

The advantage has been that the code is written exactly (more or less) as it would have been if there were no tests. There are no interfaces to add, functions to virtualize or un-static and no dependencies to add, this all translates to no performance hit in the production code and the project being simpler all around.

Re:Write tests. Not too many. Mostly integration by xab9 on Friday October 27, 2017 @02:10AM (#15566100)
Trying to "change" others' mindset is good recipe for frustration.
Re:Write tests. Not too many. Mostly integration by profalseidol on Friday October 27, 2017 @09:50AM (#15568212)
The keyword is "believe".

Such behavior is not science. In science, you don't believe, you understand not faith. You check evidence, you use reason.

A mindset that relies on "believe" but not on reason. It maybe because of decades of advertising. Can be a side-effect of participating in believe movements.

How do you change such a mindset? For me, it was reading lots of philosophy and atheist vs theist debates. For Socrates, he died for it.

Re:Write tests. Not too many. Mostly integration by diggernet on Friday October 27, 2017 @02:14AM (#15566114)
Perhaps a well written, easy to follow guide on how to structure different types of code to simplify testing would be helpful. Know of any?
Re:Write tests. Not too many. Mostly integration by arkh on Friday October 27, 2017 @03:27AM (#15566367)
Working Effectively with Legacy Code is a good read: it presents what kind of code you want to attain and methods to get there from a crappy code base.

The definition of legacy code for the author (which I like) is: untested code. So the book is more about getting code in a testable state than random refactoring to get to Clean Code level.

Re:Write tests. Not too many. Mostly integration by josteink on Friday October 27, 2017 @04:07AM (#15566537)
That book is indeed good, at least on a personal level.

It has helped me refine what I consider good code and good effort wrt to testing.

Re:Write tests. Not too many. Mostly integration by davidjnelson on Friday October 27, 2017 @02:47AM (#15566234)
Object Oriented Software Guided By Tests is good.
Re:Write tests. Not too many. Mostly integration by NotSammyHagar on Friday October 27, 2017 @03:14AM (#15566316)
Sounds like Tableau in Seattle.