Testing Frontend

lessons learned

Przemek Suchodolski
@przemuh / przemuh.pl

About me

  • @przemuh / przemuh.pl
  • Sénior Software Engineer @Egnyte
  • Frontend dev since 2012
  • 🇰🇷 korpo, 🇵🇱 software-house, 🇺🇸 startup

  • Musician amateur 🎸 🥁 🎹

  • "Gamer" / PS4 🎮 ⚽️

The journey

STEP 1

The beginning

What about testers?

STEP 2

App for testing

Assertions

expect(                       )

sum(2, 2)

.toBe(   )

4

matcher

current value

expected value

expect(2+2).toBe(4);
expect(someObject).toEqual({ x: 1, y: 2});
expect(someFunction).toHaveBeenCalled();

STEP 3

First "real' app

Ok but where are UT?

UT is missing

Code looks good but UT are missing

UT?

UT?!

UT?!!

UT?!!one

UT?!!one!!!!

🤔

Unit test

UNIT TESTING is a level of software testing where individual units / components of a software are tested...

A unit is the smallest testable part of any software.

 

Test in isolation.

describe("My awesome object", function() {

    it("has fetch method", function() {
        expect(typeof myObj.fetch).toBe("function");
    });

    describe("fetch method", function() {

        it("should call API endpoint", function() {
            spyOn(api, "awesomeEndpoint");
            
            myObj.fetch();

            expect(api.awesomeEndpoint).toHaveBeenCalled();
        });
    });
});

???

  • development ⏱  < writing tests
  • FFD (feature first development)
  • plans vs reality
  • testing implementation details (hard refactor)
  • no 🐛 protection

management

STEP 4

t D D

"How do you do that?"

Test
Driven
development

  1. Write a test ... failing test
  2. Make the test pass
  3. Refactor the code (if needed)
  4. GoTo 1st point & repeat

TDD

Red -> Green -> Refactor

"TDD is too Time Consuming.

The Business Team Would Never Approve"

"You Can’t Write Tests Until You Know the Design, & You Can’t Know the Design Until You Implement the Code"

"You Have to Write All Tests Before You Start the Code"

TDD IS HARD

but it is worth it

...

STEP 5

The pyramid

UNIT TESTS

INTEGRATION

TESTS

E2E

(end to end)

💰

😵

UNIT = 2

INTEGRATION = 0

const Counter = ({ increment, value }) => {
    const [count, setCount] = useState(0);

    return (
        <div>
            <Number value={ value } />
            <Button onClick={ increment }>+</Button>
        </div>
    );
};

UNIT

INTEGRATION

🤔

DOes it matter ?

NO!

End to end

  • No mocks / No stubs
  • Automation of user behaviour
  • Selenium / Cypress
  • Final verification
  • Sometimes unreliable
  • Could be very expensive
describe('Google demo test for Mocha', function() {

  describe('with Nightwatch', function() {

    it('uses BDD to run the Google simple test', function(browser) {
      browser
        .url('http://google.com')
        .expect.element('body').to.be.present.before(1000);

      browser.setValue('input[type=text]', ['nightwatch', browser.Keys.ENTER])
        .pause(1000)
        .assert.containsText('#main', 'Night Watch');
    });
  });
});

STEP 6

Why do we need tests?

Why do we test?

  • because someone told us?
  • because my PR will get rejected?
  • because our workflow?

confidence

UNIT TESTS

INTEGRATION

TESTS

E2E

(end to end)

💰

😵

😴

😲

STEP 7

Meet KENT

Kent c. dodds

STEP 8

TOols upgrade

  • test runner
  • react renderer
  • e2e framework

test runner

Benefits of using jest

  • running tests in parallel
  • all-in-one
  • code coverage out of the box
  • easy mocking
  • snapshot testing
  • better exceptions messages

Easy mocking

import pageVisibility from "./pageVisibility";
import createPollingAgent from "utils/pollingAgent";

jest.mock("./pageVisibility");

describe("pollingAgent", () => {

    it("does not dispatch an action when tab is not active", () => {
        jest.useFakeTimers();

        pageVisibility.isHidden.mockReturnValue(true);

        jest.runOnlyPendingTimers();

        // rest of the test ...
    });
});

Snapshot testing

it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="http://www.instagram.com">Instagram</Link>)
    .toJSON();

  expect(tree).toMatchSnapshot();
});

"Mocking is a code smell.


If you have to do a lot of mocking to create a proper unit test, maybe that code doesn’t need unit tests at all."

exports[`<SimpleTable> renders emptyState for empty data 1`] = `
<table
  class="css-1mk1rlg"
>
  <thead>
    <tr>
      <th
        class="css-k9h76t css-1sz1qrv"
      >
        column
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td
        colspan="1"
      >
        <div>
          This is custom component for empty state
        </div>
      </td>
    </tr>
  </tbody>
</table>
`;

REact TEST renderer

Why not enzyme ?

Why not shallow rendering ?

"Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behavior of child components."

"Mocking is a code smell.


If you have to do a lot of mocking to create a proper unit test, maybe that code doesn’t need unit tests at all."

E2e framework

STEP 9

what to test ?

  • TDD
  • Feature coverage
  • Good test = documentation

Next steps ?

  • find a good balance
  • visual regression testing

Takeaways

  • Test are for confidence
  • Naming doesn't matter (unit / integration / ui / functional / e2e)
  • TDD is hard but it's worth the price
  • Feature coverage > code coverage
  • Check your tools on regular basis...
  • ...and update them if needed

Thank you

Testing frontend

By Przemek Suchodolski

Testing frontend

Lessons learned

  • 281