I’m like a test unitarian. Unit tests? Great. Integration tests? Awesome. End to end tests? If you’re into that kind of thing, go for it. Coverage of lines of code doesn’t matter. Coverage of critical business functions does. I think TDD can be a cult, but writing software that way for a little bit is a good training exercise.

I’m a senior engineer at a small startup. We need to move fast, ship new stuff fast, and get things moving. We’ve got CICD running mocked unit tests, integration tests, and end to end tests, with patterns and tooling for each.

I have support from the CTO in getting more testing in, and I’m able to use testing to cover bugs and regressions, and there’s solid testing on a few critical user path features. However, I get resistance from the team on getting enough testing to prevent regressions going forward.

The resistance is usually along lines like:

  • You shouldn’t have to refactor to test something
  • We shouldn’t use mocks, only integration testing works.
    • Repeat for test types N and M
  • We can’t test yet, we’re going to make changes soon.

How can I convince the team that the tools available to them will help, and will improve their productivity and cut down time having to firefight?

1 point

Sounds like a bunch of junior engineers with senior job titles.

“Senior” is the new mid-level.

permalink
report
reply
3 points
*

Mandatory test coverage was a thing in the past, but later many teams found out that if you are forced to have like 80% test coverage, you end testing framework or even getter and setter tests (at least in Java world). Tests for the sake of tests seems to be irrelevant.

…but, test as a skill and habit is also necessary to some extent. Let’s say, you set up mandatory 20% of test coverage (for new code). This shouldn’t end with pointless tests (if it’s not only configuration, you will make much more coverage just by adding quality tests). If you have people in your team that don’t write tests at all, this requirement will get people used to writing tests.

When we no longer need to spend energy on just remembering, that we should do something (to write tests in this case), we can spend this energy on making that thing with higher quality or quicker.

Once everyone in your team is used to write tests, then is a time to start a conversation about quality of those tests, idioms, preferred patterns, aspects of readability.

PS At the beginning of my career the most important aspect of automatic testing was writing unit tests. Then sometimes people were writing some integration tests. E2E tests? What’s that? …Only now more and more I sway to the opposite side, that E2E tests are the most important ones and even in a legacy code you may not find a single unit test, but a few E2E tests cover so much. You will hate a codebase without unit tests the same, regardless if there are E2E tests or not. But if you are new to the project, there is at least a chance that those E2E tests stop you from merging code to master if it breaks core functionality. With amount of time put to write those E2E spent instead on unit tests, you won’t have this assurance. It doesn’t matter that a shared technical component in a codebase has 100% test coverage if it’s only a tiny part of core functionality.

PPS If you decide to incorporate mandatory X % of code coverage, take a look at mutation testing frameworks / tools

Mutation testing (or mutation analysis or program mutation) is used to design new software tests and evaluate the quality of existing software tests. Mutation testing involves modifying a program in small ways. Each mutated version is called a mutant and tests detect and reject mutants by causing the behaviour of the original version to differ from the mutant. – Wikipedia

permalink
report
reply
2 points

We use a little bit of property testing to test invariants with fuzzed data. Mutation testing seems like a neat inverse.

permalink
report
parent
reply
3 points

Got a bug? Write a test to proof you can reproduce it , proof you fixed it and make sure it doesn’t come back.

permalink
report
reply
12 points
*

Here’s a way to convince a team to write unit tests:

  • setup a CICD pipeline,
  • add a unit test stage,
  • add code coverage calculation,
  • add a rule where unit tests fail if a code coverage metric drops.
  • if your project is modularized, add pipeline stages to build and test and track code coverage per module.

Now, don’t set the threshold to, say, 95 %. Keep it somewhat low. Also, be consistent but not a fundamentalist.

Also, make test coverage a part of your daily communication. Create refactoring tickets whose definition of done specifies code coverage gains. Always give a status report on the project’s code coverage, and publicly praise those who did work to increase code coverage.

permalink
report
reply
9 points

Focusing on code coverage (which doesn’t distinguish between more and less important parts of the code) seems like the opposite of your very good (IMO) recommendation in another comment to focus on specific high-value use-cases.

From my experience it’s far easier to sell the need for specific tests if they are framed as “we need assurances that this component does not fail under conceivable usecases” and specially as “we were screwed by this bug and we need to be absolutely sure we don’t experience it ever again.”

Code coverage is an OK metric and I agree with tracking it, but I wouldn’t recommend making it a target. It might force developers to write tests, but it probably won’t convince them. And as a developer I hate feeling “forced” and prefer if at all possible to use consensus to decide on team practices.

permalink
report
parent
reply
5 points

Focusing on code coverage (which doesn’t distinguish between more and less important parts of the code) seems like the opposite of your very good (IMO) recommendation in another comment to focus on specific high-value use-cases.

The usefulness of code coverage ratios is to drive the conversation on the need to track invariants and avoid regressions. I agree it’s very easy to interpret a metric as a target to optimize, but in this context coverage ratios is primarily used to raise the question on why wasn’t a unit test added.

It’s counterproductive to aim for ~100% but without this indicator any question or concern regarding missing tests will feel arbitrary. With coverage ratios being tracked, this topic becomes systematic and helps build up a team culture that is test-driven or at least test-aware.

Code coverage is an OK metric and I agree with tracking it, but I wouldn’t recommend making it a target. It might force developers to write tests, but it probably won’t convince them.

True. Coverage ratios are an indicator, and they should never be an optimizable target. Hence the need to keep minimum coverage ratios low, so that the team has flexibility to manage them. Also important, have CICD pipelines export the full coverage report to track which parts of the code are not covered.

The goal is to have meaningful tests and mitigate risks, and have a system in place to develop a test-minded culture and help the team be mindful of the need to track specific invariants. Tests need to mean something and deliver value, and maximizing ratios is not it.

permalink
report
parent
reply
8 points
*

We can’t test yet, we’re going to make changes soon

This could be a good opportunity to introduce the concept of test-driven development (TDD) without the necessity to “write tests first”. But I think it can help illustrate why having tests is better when you are expecting to make changes because of the safety they provide.

“When we make those changes, wouldn’t it be great to have more confidence that the business logic didn’t break when adding a new technical capability?”

You shouldn’t have to refactor to test something

This seems like a reasonable statement and I sort of agree, in the sense that for existing production code, making a code change which only adds new tests yet also requires refactoring of existing functionality might feel a bit risky. As other commenters mentioned, starting with writing tests for new features or fixes might help prevent folks feeling like they are refactoring to test. Instead they’re refactoring and developing for the feature and the tests feel like they contribute to that feature as well.

permalink
report
reply
1 point

I think it depends. If you have to refactor in order to test, you probably built it poorly the first time around.

permalink
report
parent
reply
2 points

I think the best thing to do with TDD is pair with or convince devs to try it for a feature. Coming at things test first can be novel and interesting, and it does train you to test and use tests better. Once people have tried it, I think it broadens your use of tests pretty well.

However, TDD can be a bit of a cult, and most smart and independent people (like people willing to work at a <20 person company) will notice that TDD isn’t the silver bullet it’s proponents make it out to be.

permalink
report
parent
reply
4 points

This could be a good opportunity to introduce the concept of test-driven development (TDD) without the necessity to “write tests first”. But I think it can help illustrate why having tests is better when you are expecting to make changes because of the safety they provide.

I doubt that by now the concept of TDD is unheard of to any professional team. Name-dropping concepts actually contributes to loose credibility of any code quality effort, and works against you.

Also, TDD’s credibility is already low as it piles on the requirement of spending unordinate amounts of extra work effort on aspects of a project which don’t deliver features, and thus it’s value-added is questionable from a project management perspective.

One aspect that does work is framing the need for tests as assurance that specific invariants are verified and preserved, and thus they contribute to prevent regressions and expected failure modes. From my experience it’s far easier to sell the need for specific tests if they are framed as “we need assurances that this component does not fail under conceivable usecases” and specially as “we were screwed by this bug and we need to be absolutely sure we don’t experience it ever again.”

permalink
report
parent
reply
1 point

One aspect that does work is framing the need for tests as assurance that specific invariants are verified and preserved

Agreed - this is the specific aspect which I hoped would be communicated by studying TDD a bit!

The team is afraid that making changes will be more difficult when tests exist, but TDD (or maybe a more specific concept like you mentioned) demonstrates that tests make future changes easier.

And I specifically advocated not to follow “write tests first”.

Name-dropping concepts actually contributes to loose credibility of any code quality effort, and works against you.

OK. If I were having an in-depth discussion with my team of fellow developers to convince them to start writing tests, I don’t think that’s name-dropping.

permalink
report
parent
reply

Experienced Devs

!experienced_devs@programming.dev

Create post

A community for discussion amongst professional software developers.

Posts should be relevant to those well into their careers.

For those looking to break into the industry, are hustling for their first job, or have just started their career and are looking for advice, check out:

Community stats

  • 124

    Monthly active users

  • 76

    Posts

  • 543

    Comments