DEV Community

Mike
Mike

Posted on

5 Javascript Unit Testing Mistakes You Must Avoid in React and Angular

(My humble experience after writing 2349+ frontend tests)

Exactly 2 years, one of the developers on my team told me that my code sucked because it didn’t have 100% test coverage.

So, fair enough, I’d start listening to how much of holy grail testing was, and after listening to him, I found myself at a complete loss of words.

Everything he said made sense. That’s why all my previous applications were so buggy and hard to change.

“Hard to change” was an understatement, and “buggy” was a compliment.

Every time I wanted to introduce a change to the code base, it felt like inserting a piece into a continent landmass-sized house of dominoes.

You know, fixing a bug by creating 10 other bugs kind of situation.

By the time I finished listening to my friend, I couldn’t hold it anymore.

I stopped typing the code I was supposed to deliver tomorrow and typed “How to Test FrontEnd applications” into Google.

What I discovered blew my mind.

Anyway, fast-forward 4 years later, and I’ve developed my personal “do’s and dont’s” to testing frontend applications. It only took writing 2349+ tests of varying “usefulness” to realize that.

So here they are. My top 5 mistakes you or your team should avoid when writing frontend tests.

1. You Are Postponing Tests for “Later”, which never comes

A lot of developers don’t like writing front end tests.
Actually, some straight-up hate them and would rather become a Cobol developer than write a single HTML / Javascript test.

Here are a few seemingly logical reasons:
“Testing slows down the development!”
“If my code is good, I don’t need to test it! Let the QA do the testing job”
“I tried front end tests before, and they were absolutely useless!”

Logical is a dangerous word. It is logical based on their previous experience with tests, and usually, that experience hasn’t been good.

You see, automated tests have a lot of benefits :

Benefit #1: You are able to come back to your previous code after a few months and change it with minimal fear.

New requirements came in? If you have good tests, you will have good confidence that the modified system works as intended.

Suddenly realised that your architecture was subpar? It is very easy to change up your code without affecting previous end-user experience and functionality.

Benefit #2: New developers can more confidently change up code without breaking the system as a side-effect

Let’s say you have a sign-up flow with a bunch of edge cases, and a new developer comes in.

The best good case is he or she will ask the team a bunch of questions, then the code will go through the PR process, and the other developers will actually take the time to review and test the code to avoid bugs.

But in reality, your team are people, and bugs will slip through despite the best PR review processes.

Besides, there are enough teams where code reviews are not even that strict, and you can get a “LGTM!” If you ask nicely enough.

This is why developers should put effort into writing good tests.The type of tests that will solidify the existing app behavior, validate a few edge cases, check some variations, and run predictably each time you or anyone else makes changes to the code base.

2. Stop Testing Your Implementation

You might be asking…

“Mike, what the hell do you mean when you say I should stop testing implementation?”

Well, let’s say we have this component.

class SomeComponent {
    @Output viewUpdated = new EventEmitter(); 

    clickButton = () =>  {
      fetch(‘…/../‘).then(r => r.json()).then(() => this.updateView());
    } 

    updateView = () => {
      this.viewUpdated.emit(true);
    }
} 
Enter fullscreen mode Exit fullscreen mode

Do you find this test useful?

describe(’SomeComponent’, () => {
/** Setup code… */
    it(‘should react to clicking the button’, fakeAsync(() => {
        const updateViewSpy = spyOn(component, ‘updateView’);
        component.clickButton();
        tick();
        expect(updateViewSpy).toHaveBeenCalled();
    }));
}); 
Enter fullscreen mode Exit fullscreen mode

Hm, well… do you?

It is not useful, and let me tell you a few reasons why,:

It tests that a button was clicked, but honestly, it doesn’t test that “clickButton” was wired correctly to the HTML. It is too “shallow” and tied to the Typescript/Javascript implementation.

It is testing that calling one component method evokes another “method”… That is too “tangled” to the Typescript/Javascript implementation. You literally are testing that “the code I wrote is the way I wrote it”.

That is not how the user uses your system. The user doesn’t “expect” that a component’s internal method was called when another method was called. (Of course, other developers can be your users, but they would probably care more that an event was emitted.

I think this is the biggest problem with that test. The test is validating that:
“The code I wrote the code is the way I wrote the code.”

The problem with testing your “implementation” is:

  1. These tests are way too brittle to any implementation changes or re-factoring
  2. These tests don’t even test that the system behaves as expected to the user.
  3. If someone tries to read your test to understand how the system behaves, he or she’ll probably waste a lot of time without learning much. Also, what if that test above where one method calls another method is hiding a bug?

For example, the API URL could be incorrectly supplied, or the internal work of the methods was incorrectly updating the component’s state. Either of that happens, and you get a “false positive” – a test that is passing even though the system is buggy.

Time and time again, I’ve found that whenever I wrote tests that tested how I wrote my classes or functions I wasn’t helping with identifying or preventing any bugs.

I’ve learned that the mindset during testing should instead be:

“Regardless of the way I wrote the code, the app satisfies the business requirements and behaves as expected to the user.”

See how I added the phrase “Regardless of the way I wrote the code”?

That’s the critical part of “resilient” tests.

Full article and remaining points available at: https://www.effortlesscoding.com/javascript-unit-testing-mistakes/

Top comments (1)

Collapse
 
dance2die profile image
Sung M. Kim

Hi Mike,

We encourage the entire article to be published on DEV.to (if you have proper rights), with a linkback if appropriate. Otherwise, we recommend original material, such as an original commentary on the article. From the Terms of Use:

Users must make a good-faith effort to share content that is...not designed primarily for the purposes of promotion or creating backlinks. Additionally, posts must contain substantial content — they may not merely reference an external link that contains the full post.

Posts that are simply intended to encourage readers to view an external resource are discouraged.

Thank you.