frontend testing

frontend testing

As an SWE, you will need to write tests… I don’t think there is a way around it. Some companies have QA write out test plans but feature wise, the engineer working on the feature is responsible for writing the tests documented in the test plan.

This is just a brief intro to frontend testing. I there won’t be any code examples or anything, just high-level overview on what testing looks like in the frontend and why it’s crucial in every frontend project.

for me, it is testing, what is it

There are many types of tests that can go into a project, but frontend tests can be exceptionally confusing.

In my opinion, there are 4 different types of tests that should be in most frontend projects. They are listed from granular to broad.

1) Unit Testing

2) Integration Testing

3) End-to-end Testing

unit testing

Most people are aware of what unit testing is. This concept is used in most coding disciplines.

This is the lowest level of testing and it’s testing the smallest amount of code.

You have a component, you write a test that tests the component and how it should render, etc, etc.

Why it’s necessary.

  • The main reason for unit tests for a component is to give confidence in changing the component. If a component is changed in a way that breaks it, the tests will fail.

  • Debugging is made easy. When a component breaks, if the unit tests are written properly, it should be simple to find the bug and fix it.

  • Code is more reliable.

Example:

1
expect(isCompleted()).to.be(true)

integration testing

Integration testing is basically a more broad unit testing. Integration testing tests how components integrate with each other. These test should reveal flaws/edge cases when components interact with each other.

You can have 2 components that theoretically individually function properly, but there are bugs with the 2 integrated together.

Why it’s necessary.

  • the main reason for integration testing is similar to unit testing. it should give developers confidence in changing a component. If a component is changed and it breaks another component, then something went wrong.

Example:

1
2
3
4
5
6
7
8
9
const appWrapper = mount(<ToDoApp />);

expect(wrapper.exists(".todo-input-field")).to.equal(true);

appWrapper.find(".todo-add-button").simulate("click");

const newTodo = wrapper.find(".todo-list-item").at(0);

expect(wrapper.exists(newTodo));

end-to-end testing

Out of all the prior testing, I believe end-to-end testing is the most important probably the hardest.

End-to-end testing is testing the entire flow of an application and it needs to be done in a comprehensive way in order for the tests to be useful. End-to-end tests should cover testing in all environments that the application supports including all browsers and devices.

For a web application, I would strongly looking into NightWatch, Phantom and Cypress. These are all browser controllers that run tests on actual browsers and goes through defined flows. These libraries allow engineers to programmatically write out these flows.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'step one: navigate to google' : function (browser) {
browser
.url('https://www.google.com')
.waitForElementVisible('body', 1000)
.setValue('input[type=text]', 'nightwatch')
.waitForElementVisible('input[name=btnK]', 1000)
},

'step two: click input' : function (browser) {
browser
.click('input[name=btnK]')
.pause(1000)
.assert.containsText('#main', 'Night Watch')
.end();
}

Pulled from NightWatch site: https://nightwatchjs.org/guide.

for me, it is testing, the pain in my butt

Testing is one of those things that sound great on paper and can wow people in meetings but is a nightmare when it comes to execution.

I’ve seen my share of crappy tests that are better off just not being written in the first place. I’ve also worked on projects that have a testing process and requirements, but it’s very loosely followed rendering testing useless. Most projects you work on will be one of the two or somewhere in-between.

There are many reasons why a project has bad or nonexistent testing, but the main reason is simply not wanting to write tests.

I use to be on that boat and every now and then I still hop back on that boat.

Testing is a pain in my butt.

for me, it is testing, the most important thing to do

I didn’t appreciate testing until my first big bug. I was doing a major refactor to share code between desktop and mobile app. In the end, I ended up removing 1,500 lines of code. It was a very big refactor that touched almost every component of the app, I was very proud of myself. I tested, or at least I thought I tested. Learn from my mistake.

what broke

Around a month after the refactor, we were set for launch. 2-3 weeks before the launch, QA found a lot of bugs around the routing around the mobile app. Clicking the back button, often brought users somewhere that was not the previous route.

Since my refactor touched most of the app, it was logical that I was to blame.

was it my refactor

I scoured through my code, and as any proud engineer would do, didn’t find anything wrong with it.

I touched routing for sure, but I only combined the same routes into one routes file instead of having two route files, one for mobile and one for desktop.

My changes shouldn’t have caused any issues. However, there was nothing to back up my claim. No tests.

didn’t write tests

My major mistake, refactoring without writing tests….. The conclusion of what broke the app could have been reached much faster if I wrote comprehensive tests before I refactored. To begin with, the project didn’t have a good amount of unit tests and e2e tests. There were some, but definitely not enough.

I had done a huge refactor and had no new tests to test the components and functionality. This was a huge screw up on my part and was the main reason I was not able to debug this issue correctly. The more I thought about it, the more doubt came into my mind that it WAS my refactor that broke the routing.

The whole web team had to stop their work and spend a whole week going through every possible flow of the app to test the routing and make note of all the places where there was an issue.

PR was too big

My secondary mistake was having a PR that was way too big. 500 lines is a lot of code for PR and a lot of code for someone to review. On top of that, removing 1,500 lines of code made it an even bigger task.

Looking back, I think the PR review was rushed a bit since everyone was busy and it was such a big PR.

Having PR that are small and commits that are small allow others to review and give feedback easier. I should have created a “feature” refactor branch and I should have branched off of that and done incremental PRs into the “feature” branch and asked for a review.

conclusion

To cut the story short, the issue was actually not my refactor. It was because of an iOS update that was a breaking change to the native WebView which broke the way the WebView history was working.

However, I was still to blame. This issue could have been caught a lot sooner and a lot faster if there were better unit tests around the components, more integration testing, and comprehensive E2E testing on all devices and browsers that were supported. Specifically the E2E testing on actual devices.

With automated testing on actual devices, we would have seen passing tests up until the iOS update, then it would have pinpointed the issue immediately and allowed the team to skip the days of debugging and jump straight into the problem

for me, it is testing, my true love

I’d like to think I’ve grown a lot as an engineer since that incident. I’m writing a lot more tests and I’m currently doing a more Test Driven Development. My time is spent probably 40% on writing tests, 30% on actual development, and 30% on pulling my hair out when my dev doesn’t pass my tests.

That’s love, right?