Slide 1

Slide 1 text

UNIT TESTING IM FRONTEND Klingt komisch. Is aber so. MIRJAM BÄUERLEIN | @MIRJAM_DIALA

Slide 2

Slide 2 text

I ! Testing

Slide 3

Slide 3 text

WER TESTET SEINEN CODE?

Slide 4

Slide 4 text

WER TESTET SEINEN CODE IM FRONTEND?

Slide 5

Slide 5 text

Browser neuladen zählt dazu...

Slide 6

Slide 6 text

Was ist eigentlich... AUTOMATISIERTES SOFTWARE TESTING?

Slide 7

Slide 7 text

UNIT TESTS

Slide 8

Slide 8 text

INTEGRATION TESTS

Slide 9

Slide 9 text

END TO END TESTS

Slide 10

Slide 10 text

END TO END INTEGRATION UNIT TESTS

Slide 11

Slide 11 text

Muss ich testen?

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

"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

Slide 14

Slide 14 text

"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

Slide 15

Slide 15 text

ACTUAL FOOTAGE OF ME SEEING THIS RESULT

Slide 16

Slide 16 text

Historisch gewachsen FRONTEND !== FRONTEND ANYMORE

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

But how? UNIT TESTING IM FRONTEND

Slide 21

Slide 21 text

Beispiel 1 FORMATTER VANILLA JS

Slide 22

Slide 22 text

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; });

Slide 23

Slide 23 text

document.addEventListener("DOMContentLoaded", function() { var sumElement = document.querySelector(".js-sum") var unformatedSum = sumElement.innerHTML; var formatedSum = moneyFormatter(unformatedSum) sumElement.innerHTML = formatedSum; });

Slide 24

Slide 24 text

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('') }

Slide 25

Slide 25 text

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'); }

Slide 26

Slide 26 text

Beispiel 2 SUBMIT BUTTON JQUERY, ES6

Slide 27

Slide 27 text

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 = '.'; element.append(loadingDotHtml).append(loadingDotHtml).append(loadingDotHtml); } function formProcessingAnimation(button) { button.addClass('processing'); button.text('Processing'); appendLoadingDots(button); } export default { registerFormSubmit };

Slide 28

Slide 28 text

describe('register form submit', function() { const registerFormSubmit = formHelpers.registerFormSubmit; let testForm = null; beforeEach(function() { testForm = $('').append('Hello'); $('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); }); });

Slide 29

Slide 29 text

Beispiel 3 LINK COMPONENT REACTJS

Slide 30

Slide 30 text

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 ( {children} ) } export default ButtonExternalLink

Slide 31

Slide 31 text

const testUrl = 'http://forum.conferencebuddy.io/' const linkText = 'Test Button Linktext' const linkImage = describe('', () => { it('renders without errors', () => { expect(() => shallow( ) ).not.toThrow() }) it('renders without console errors', () => { expect(() => shallow( {linkText} ) ).not.toConsoleError() }) })

Slide 32

Slide 32 text

describe('renders a link with an url and the "btn" class', () => { const wrapper = shallow( {linkText} ) 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( {linkText} ) expect(wrapper.text()).toBe(linkText) }) it('renders a link with an image as children', () => { const wrapper = shallow( {linkImage} ) expect(wrapper.contains(linkImage)).toBe(true) }) }) describe('can style specific looks "primary" and "secondary"', () => { it('adds a class for style primary', () => { const wrapper = shallow( {linkText} ) expect(wrapper.hasClass('btn-primary')).toBe(true) expect(wrapper.hasClass('btn-secondary')).toBe(false) }) it('adds a class for style secondary', () => { const wrapper = shallow( {linkText} ) expect(wrapper.hasClass('btn-secondary')).toBe(true) expect(wrapper.hasClass('btn-primary')).toBe(false) }) })

Slide 33

Slide 33 text

TESTS ARE CODE.

Slide 34

Slide 34 text

DANKE! ! [email protected] " @mirjam_diala # programmiri