Unit Testing im Frontend - Lightning Talk

6fe15a7e096eb9517a201a249e57aa80?s=47 Mirjam
November 29, 2018

Unit Testing im Frontend - Lightning Talk

Slides to my (german) Talk at the Cosee TechTalks.

6fe15a7e096eb9517a201a249e57aa80?s=128

Mirjam

November 29, 2018
Tweet

Transcript

  1. UNIT TESTING IM FRONTEND Klingt komisch. Is aber so. MIRJAM

    BÄUERLEIN | @MIRJAM_DIALA
  2. I ! Testing

  3. WER TESTET SEINEN CODE?

  4. WER TESTET SEINEN CODE IM FRONTEND?

  5. Browser neuladen zählt dazu...

  6. Was ist eigentlich... AUTOMATISIERTES SOFTWARE TESTING?

  7. UNIT TESTS

  8. INTEGRATION TESTS

  9. END TO END TESTS

  10. END TO END INTEGRATION UNIT TESTS

  11. Muss ich testen?

  12. None
  13. "DOES YOUR TEAM WRITE TESTS FOR BACK-END CODE?"1 1 State

    Of The Web Survey, August 2018, https://dev.to/devteam/state-of-the-web-data---call-for-analysis-2o75
  14. "DOES YOUR TEAM WRITE TESTS FOR FRONT-END CODE?"1 1 State

    Of The Web Survey, August 2018, https://dev.to/devteam/state-of-the-web-data---call-for-analysis-2o75
  15. ACTUAL FOOTAGE OF ME SEEING THIS RESULT

  16. Historisch gewachsen FRONTEND !== FRONTEND ANYMORE

  17. None
  18. None
  19. None
  20. But how? UNIT TESTING IM FRONTEND

  21. Beispiel 1 FORMATTER VANILLA JS

  22. document.addEventListener("DOMContentLoaded", function() { var unformatedSum = document.querySelector(".js-sum").innerHTML; var sumNum =

    unformatedSum.toString().split('').reverse(); var sumFormatted = []; for (var i = 0; i < sumNum.length; i++) { var currNum = sumNum[i]; if (i != 0 && i % 3 == 0) { sumFormatted.push('.'); } sumFormatted.push(currNum); } if (sumFormatted.length > 0) { sumFormatted.reverse(); sumFormatted.push(' Euro'); } var formatedSum = sumFormatted.join(''); document.querySelector(".js-sum").innerHTML = formatedSum; });
  23. document.addEventListener("DOMContentLoaded", function() { var sumElement = document.querySelector(".js-sum") var unformatedSum =

    sumElement.innerHTML; var formatedSum = moneyFormatter(unformatedSum) sumElement.innerHTML = formatedSum; });
  24. function moneyFormatter(sum) { var sumNum = sum.toString().split('').reverse(); var sumFormatted =

    []; for (var i = 0; i < sumNum.length; i++) { var currNum = sumNum[i]; if (i != 0 && i % 3 == 0) { sumFormatted.push('.'); } sumFormatted.push(currNum); } if (sumFormatted.length > 0) { sumFormatted.reverse(); sumFormatted.push(' Euro'); } return sumFormatted.join('') }
  25. function assertEqual(actual, expected) { if (expected === actual) { console.info('[SUCCESS]

    Is ' + expected); } else { console.error('[ERROR] Expected ' + actual + ' to be ' + expected); } } function tests() { assertEqual(formatSum(1), '1 Euro'); assertEqual(formatSum(12), '12 Euro'); assertEqual(formatSum(123), '123 Euro'); assertEqual(formatSum(1234), '1.234 Euro'); assertEqual(formatSum(12345), '12.345 Euro'); assertEqual(formatSum(123456), '123.456 Euro'); assertEqual(formatSum(1234567), '1.234.567 Euro'); }
  26. Beispiel 2 SUBMIT BUTTON JQUERY, ES6

  27. function registerFormSubmit() { $('body').on('submit', handleFormSubmit); } function handleFormSubmit(event) { const

    submittedButton = $(event.target).find('[type="submit"]'); submittedButton.addClass('disabled'); formProcessingAnimation(submittedButton); } function appendLoadingDots(element) { const loadingDotHtml = '<span class="loading-dot">.</span>'; element.append(loadingDotHtml).append(loadingDotHtml).append(loadingDotHtml); } function formProcessingAnimation(button) { button.addClass('processing'); button.text('Processing'); appendLoadingDots(button); } export default { registerFormSubmit };
  28. describe('register form submit', function() { const registerFormSubmit = formHelpers.registerFormSubmit; let

    testForm = null; beforeEach(function() { testForm = $('<form></form>').append('<button type="submit">Hello</button>'); $('body').append(testForm); registerFormSubmit(); }); afterEach(function() { testForm.remove(); }); it('defines a registerFormSubmit function', function() { expect(typeof registerFormSubmit).toBe('function'); }); it('disables the button on submit', function() { expect(testForm.find('button')).not.toHaveClass('disabled'); testForm.submit(); expect(testForm.find('button')).toHaveClass('disabled'); }); it('adds class "processing" on submit', function() { expect(testForm.find('button')).not.toHaveClass('processing'); testForm.submit(); expect(testForm.find('button')).toHaveClass('processing'); }); it('replaces the button text with "Processing"', function() { expect(testForm.find('button')).toContainText('Hello'); testForm.submit(); expect(testForm.find('button')).toContainText('Processing...'); }); it('adds three dots on submit', function() { expect(testForm.find('button').find('.loading-dot')).toHaveLength(0); testForm.submit(); expect(testForm.find('button').find('.loading-dot')).toHaveLength(3); }); });
  29. Beispiel 3 LINK COMPONENT REACTJS

  30. const ButtonExternalLink = ({ url, children, primary, secondary, small })

    => { const btnCustomClass = small ? 'btn-custom-small' : 'btn-custom' const primaryClass = primary ? ' btn-primary' : '' const secondaryClass = secondary ? ' btn-secondary' : '' return ( <a className={`btn ${btnCustomClass}${primaryClass}${secondaryClass}`} href={url} > {children} </a> ) } export default ButtonExternalLink
  31. const testUrl = 'http://forum.conferencebuddy.io/' const linkText = 'Test Button Linktext'

    const linkImage = <img src="test.jpg" /> describe('<ButtonExternalLink />', () => { it('renders without errors', () => { expect(() => shallow( <ButtonExternalLink ></ButtonExternalLink> ) ).not.toThrow() }) it('renders without console errors', () => { expect(() => shallow( <ButtonExternalLink>{linkText}</ButtonExternalLink> ) ).not.toConsoleError() }) })
  32. describe('renders a link with an url and the "btn" class',

    () => { const wrapper = shallow( <ButtonExternalLink url={testUrl}>{linkText}</ButtonExternalLink> ) it('renders a link element', () => { expect(wrapper.find('a')).toHaveLength(1) }) it('renders a link with a given url', () => { expect(wrapper.find('a').prop('href')).toEqual(testUrl) }) it('renders a link with a class', () => { expect(wrapper.hasClass('btn')).toBe(true) }) }) describe('renders a link with a given content', () => { it('renders a link with a text as children', () => { const wrapper = shallow( <ButtonExternalLink url={testUrl}>{linkText}</ButtonExternalLink> ) expect(wrapper.text()).toBe(linkText) }) it('renders a link with an image as children', () => { const wrapper = shallow( <ButtonExternalLink url={testUrl}>{linkImage}</ButtonExternalLink> ) expect(wrapper.contains(linkImage)).toBe(true) }) }) describe('can style specific looks "primary" and "secondary"', () => { it('adds a class for style primary', () => { const wrapper = shallow( <ButtonExternalLink url={testUrl} primary> {linkText} </ButtonExternalLink> ) expect(wrapper.hasClass('btn-primary')).toBe(true) expect(wrapper.hasClass('btn-secondary')).toBe(false) }) it('adds a class for style secondary', () => { const wrapper = shallow( <ButtonExternalLink url={testUrl} secondary> {linkText} </ButtonExternalLink> ) expect(wrapper.hasClass('btn-secondary')).toBe(true) expect(wrapper.hasClass('btn-primary')).toBe(false) }) })
  33. TESTS ARE CODE.

  34. DANKE! ! mirjam.baeuerlein@cosee.biz " @mirjam_diala # programmiri