DEV Community

Cover image for Unit-Testing: The Unsung Hero of Code Quality 🛡️

Unit-Testing: The Unsung Hero of Code Quality 🛡️

Rahul Ladumor on October 29, 2023

Why Skipping Unit Tests is Like Jumping Out of an Airplane Without a Parachute 🪂 Hey folks! Today, we're diving deep into the world of u...
Collapse
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix • Edited

I do not think it is intimidating, in total you only need to know 2 methods

  • "test" - an empty wrapper that does nothing, only giving a separate name to the test and
  • "expect" - to compare values, comparing methods can be chained / auto-completed after calling expect on an object/value.

Unit test best practice:

  • write logic in functions, only using object state (no library stuff, components or anything else that has to be mocked)
  • write/commit explicit small test data: common cases and all edge cases
  • write core logic in a functional style
  • if you have to mock anything, then you are doing it wrong. Mocks significantly lower/decrease/slow down your feedback loop and make you hate tests. If tests run fast or instantly it is not a hassle but a joy to use instead.

About time consuming -> I would argue that you just pay upfront the time you would have spend debugging the same edge cases.

  • You can either discover them (bugs, edge cases) yourself, or let them be discovered by your tester or (worst case) your users.
  • You just have to explicitly model (and think about), and write down (more easily done with a functional programming style) all edge cases.
  • You have to move everything logic based outside of components and inside pure functions

That is hard -> getting typescript tests to work. That is now easily solved by Bun.

I would recommend not testing components, reasons:

  • high complexity, have to change coding style, etc
  • hard to setup tests
  • do not really test visual stuff, ex: like is this button in view or somewhere outside?
  • instead just skip component tests and use e2e tests with playwright instead, easily generated with a multitude of visual recording tools
  • playwright even allows for visual diffs, ex.like why is this button suddenly red instead of blue
Collapse
 
rahulladumor profile image
Rahul Ladumor

Hey there! Thanks for taking the time to share such a detailed perspective! 😄

You've raised a lot of great points. The simplicity of the test and expect methods is indeed a blessing for those getting into unit testing, particularly with Jest in a Node.js environment. It really helps to keep the cognitive load low.

On Writing Logic in Functions

Absolutely agree! Keeping your logic inside pure functions makes it way easier to test. This aligns well with SOLID principles, particularly the Single Responsibility Principle. The more modular your code, the easier it is to test, understand, and maintain.

On Mocking

Your point on mocking is thought-provoking. In a perfect world, we'd minimize the need for mocks, but sometimes with complex systems—especially in a cloud environment like AWS—you might need to mock services to isolate the unit of work. However, I do agree that excessive mocking could slow down the feedback loop and introduce complexity.

On Time Investment

I couldn't agree more. The time spent writing tests can often offset the time you'd otherwise spend debugging. It's all about paying it forward.

On TypeScript Tests

Thanks for the shout-out to Bun for TypeScript testing! It's always good to have easier solutions to complicated problems.

On Component Testing

Interesting viewpoint on skipping component tests. While component tests have their limitations, I feel they still add some value, especially for larger teams and projects. But yes, E2E tests with tools like Playwright can cover a lot of what component tests do, plus give us visual diffs.

I really appreciate the insights you've provided. It definitely adds another layer to the discussion, and I'm sure others will find it valuable too! 🙌

So what are your thoughts on balancing unit tests with integration tests and E2E tests in a CI/CD pipeline, especially when using DevOps tools?

Cheers! 🚀

Collapse
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix

E2E are integration tests.
No other way can you test integration, E2E-Tests are the only way.

Even with Next.js (Frontend and Backend in one) you still depend on and call external services, db.
If any of your external services (or db) does not behave how you expected it (api changes or anything else) -> your entire app does not work. Examples: db, network, email, payment, not enough resources on the vps, etc.

Testing-Balance

  • Obviously you do not need to write any tests if you are prototyping and just want to show something. The moment you ship something to users:
  • You do maximum unit tests.
  • You do e2e tests for most primary/common use cases, because debugging and maintaining e2e tests is more expensive and time consuming. If your website or web app changes offen (user workflow) -> you should have only a small amount of e2e tests, because then you have to update the tests offen as well -> that hurts greatly if you have many of them. If your user workflow changes less often then you can go all out and "lock everything down" or "cement" into tests.

The Balance highly depends on the competence of your devs/team.

  • If you have a team of many inexperienced juniors -> then you have to obviously increase your test to be confident nothing breaks down then it hits production. This leads to a more defensive development style.
  • If you have team members, who are highly competent -> then you can skip many ci/cd/devops tools completely, since everyone of them should/would have run and tested it locally. Errors might still happen, but you know that they will be resolved quickly. This leads to a more offensive development style.
Thread Thread
 
rahulladumor profile image
Rahul Ladumor

E2E as Integration Tests

I couldn't agree more. E2E tests are essentially the ultimate integration tests. They simulate real-world scenarios, making sure that all the cogs in your system mesh perfectly. This is crucial when you're relying on external services or databases; things can break unpredictably when dependencies change, just as you've pointed out.

Testing Balance

Your breakdown on when and how much to test is on point. Prototyping doesn't require exhaustive tests, but the game changes when you're shipping to users.

  • Unit Tests: You rightly emphasized their importance for capturing the maximum number of edge cases.

  • E2E Tests: They are resource-heavy but indispensable for primary/common use-cases. Totally agree that their quantity should be proportional to how often the user workflow changes.

Team Competence and Testing

Love this point. The competence level of the dev team plays a huge role in determining the testing strategy. Juniors may require a safety net of extensive testing, while a team of seasoned devs might be able to get by with lighter testing, as they're likely running extensive tests locally.

You summed it up perfectly; it's a balance that's influenced by multiple factors including the competency of the team, the stability of the user workflow, and the nature of your application's dependencies.

In light of your insights, what tools and frameworks do you recommend for optimizing this testing balance, especially when you're in an agile environment where things can change rapidly?

Thanks again for enriching this discussion! 🚀

Thread Thread
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix • Edited
  • I like the Buns build-in test runner. I have ported all my tests from jest and I'm completely happy with it.
  • I test and develop all my components visually with a specially build page (similar to storybook) there I can toggle/circle all its states with mock data and debug it.
  • E2E tests - with playwright.
Thread Thread
 
rahulladumor profile image
Rahul Ladumor

Hey, thanks for sharing your toolkit! 🛠️

It's great to hear that you're loving Bun's built-in test runner and that it's meeting all your needs after migrating from Jest. That's a significant move and speaks volumes about its capabilities!

Your approach to component testing is super interesting, kind of like a homegrown Storybook. Testing components with actual visual scenarios and mock data is a solid practice, for sure.

As for E2E, Playwright is an excellent choice. It's incredibly powerful for automating browser tasks and conducting visual tests.

Just a heads-up, though: Bun might not be the best fit for everyone, especially those heavily invested in serverless architectures. But for those who can use it, sounds like it's working wonders for you.

Given your extensive experience, do you have any tips on managing test data? How do you usually go about setting up, tearing down, or updating mock data in your tests?

Cheers! 🚀

Collapse
 
artxe2 profile image
Yeom suyun

coding without unit testing is like playing Russian roulette with your project.

I agree with you to some extent, even though it is a bit extreme.
However, I am disappointed that vitest is not included in the recommended test libraries.

Collapse
 
rahulladumor profile image
Rahul Ladumor

Hi @artxe2 Firstly, I appreciate you taking the time to read the article and sharing your perspective. I agree that everyone has their own experiences and preferences when it comes to development practices.

Regarding vitest, you're absolutely right! It's indeed a valuable testing library, and I'm grateful you brought it to my attention. I focused on some of the more widely known libraries in the article, but I acknowledge that there are many fantastic tools out there, including vitest. I'll definitely consider diving deeper into it and perhaps even feature it in a future piece. Your feedback helps in making the content better and more inclusive for everyone. Thanks again! 🙌

Warm regards,
Mr. Rahul

Collapse
 
tracygjg profile image
Tracy Gilmore

Hi Rahul,
I very much agree with your post. I think Unit test is actually less about testing and more about proving developer intent. That is why I wrote a post on this theme. I hope you get chance to read it and please give me your thoughts.

I heard it said, "If your are refactoring code without unit tests your are not refactoring, you are just changing code" and IMO you get what you deserve!
Regards, Tracy

Collapse
 
rahulladumor profile image
Rahul Ladumor • Edited

Hi @tracygjg ,

Thank you for your comment! It's always gratifying to meet someone who shares similar viewpoints. I completely agree—unit tests indeed go beyond just "testing"; they're about affirming that the code does what it's intended to do.

Best regards,
Mr. Rahul

Collapse
 
nhathuoc115com profile image
Nhà thuốc 115 thuoc115.com

Your source code is really good.

Collapse
 
fetidd profile image
Ben Jones

Unsung? Seriously?

Some comments have been hidden by the post's author - find out more