Younes Jaaidi

@yjaaidi

🇫🇷 Lyon

eXtreme Programming since 2007

Web Dev & XP Coach

👨🏻‍🏫 Web Dev, XP & Security Trainings

@yjaaidi

👨🏻‍🏫 Web Dev, XP & Security Trainings

🎯 Consulting & XP Coaching

@yjaaidi

👨🏻‍🏫 Web Dev, XP & Security Trainings

💻 Remote Consulting Sessions

🎯 Consulting & XP Coaching

@yjaaidi

Angular Testing Workshop

@yjaaidi

@yjaaidi

Do we really
need tests?

🤖
Automated tests

@yjaaidi

🤖
Automated tests

🐞
Less bugs

@yjaaidi

🤖
Automated tests


Quality

🐞
Less bugs

Not Visible

Visible

@yjaaidi

🤖
Automated tests


Quality

🐞
Less bugs

Not Visible

Visible

🥵
Stop testing
every change
manually

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests


Quality

🐞
Less bugs

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests


Quality

🐞
Less bugs

🐇
Faster time
to market

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🐇
Faster time
to market

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🐇
Faster time
to market

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🐇
Faster time
to market

⚽️
Collective
ownership

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🐇
Faster time
to market

⚽️
Collective
ownership

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

♻️
Update
dependencies

automatically
(or frequently)

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

♻️
Update
dependencies

automatically
(or frequently)

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

♻️
Update
dependencies

automatically
(or frequently)

Not Visible

Visible

@yjaaidi

🥵
Stop testing
every change
manually

⚡️
Faster

development

🤖
Automated tests

💪
Confidence


Quality

🐞
Less bugs

🛠
Refactoring

🐇
Faster time
to market

⚽️
Collective
ownership

♻️
Update
dependencies

automatically
(or frequently)

🥳
Pleasure

Not Visible

Visible

@yjaaidi

Angular Hello World Dependencies

~1 000 dependencies

@yjaaidi

Angular Hello World Dependencies

🏋️‍♀️ ~300MB 🏋️‍♂️

~1 000 dependencies

@yjaaidi

Angular Hello World Dependencies

🏋️‍♀️ ~300MB 🏋️‍♂️

~1 000 dependencies

~3.5 million lines of code

@yjaaidi

Tests are the best documentation

@yjaaidi

🚀 Up-to-date

Tests are the best documentation

@yjaaidi

🦋 Symmetric
you get the exact doc of the version you are using

🚀 Up-to-date

Tests are the best documentation

@yjaaidi

🌏 More surface

🦋 Symmetric
you get the exact doc of the version you are using

🚀 Up-to-date

Example: RxJS Count Operator

describe('count operator', () => {
  it('should count the values of an observable', () => {
    const source = hot('--a--b--c--|');
    const expected =   '-----------(x|)';

    expectObservable(source.pipe(count())).toBe(expected, {x: 3});
  });

  it('should be never when source is never', () => {
    const e1 =  cold('-');
    const expected = '-';

    expectObservable(e1.pipe(count())).toBe(expected);
  });

@yjaaidi

asDiagram('count')('should count the values of an observable', () => {
  const source = hot('--a--b--c--|');
  const expected =   '-----------(x|)';

  expectObservable(source.pipe(count())).toBe(expected, {x: 3});
});

@yjaaidi

@yjaaidi

☁️

Frontend

Backend

Database

Other Services

Unit Testing

@yjaaidi

☁️

Frontend

Backend

Database

Other Services

Integration Testing

Unit Testing

@yjaaidi

☁️

Frontend

Backend

Database

Other Services

E2E Testing (or Functional Testing)

Integration Testing

Unit Testing

@yjaaidi

☁️

Frontend

Backend

Database

Other Services

🧹 E2E Testing

🚀 Test a feature or set of features

🤖 Browser automation

✅ Happy path or smoke tests

@yjaaidi

🧹 E2E Testing

🚀 Test a feature or set of features

it('should search using emojis', () => {

  searchPage.go();
    
  searchPage.search('🍺');
    
  const resultList = searchPage.getResultList();
    
  expect(resultList.length).toBeGreaterThan(0);
  expect(resultList[0].title).toContain('Beer');

});

🤖 Browser automation

✅ Happy path or smoke tests

@yjaaidi

🧹 E2E Testing

🚀 Test a feature or set of features

it('should search using emojis', () => {

  searchPage.go();
    
  searchPage.search('🍺');
    
  const resultList = searchPage.getResultList();
    
  expect(resultList.length).toBeGreaterThan(0);
  expect(resultList[0].title).toContain('Beer');

});

🤖 Browser automation

✅ Happy path or smoke tests

💵 Cheaper

🤝 Test interactions

😊

⛵️ Loosely coupled to implementation

@yjaaidi

🧹 E2E Testing

🚀 Test a feature or set of features

it('should search using emojis', () => {

  searchPage.go();
    
  searchPage.search('🍺');
    
  const resultList = searchPage.getResultList();
    
  expect(resultList.length).toBeGreaterThan(0);
  expect(resultList[0].title).toContain('Beer');

});

🤖 Browser automation

✅ Happy path or smoke tests

💵 Cheaper

🐢 Slower

🐞 Harder to debug

🤝 Test interactions

😊

😔

⛵️ Loosely coupled to implementation

@yjaaidi

🖌 Unit Testing

🐁 Test a small piece of code

🎯 Precision

@yjaaidi

🖌 Unit Testing

🐁 Test a small piece of code

🎯 Precision

@yjaaidi

it('should parse price', () => {

  const price = parsePrice('30.01€');

  expect(price).toEqual({
    coefficient: 3001,
    exponent: -2,
    currency: 'EUR'
  });

});

🖌 Unit Testing

🐁 Test a small piece of code

🎯 Precision

😊

@yjaaidi

it('should parse price', () => {

  const price = parsePrice('30.01€');

  expect(price).toEqual({
    coefficient: 3001,
    exponent: -2,
    currency: 'EUR'
  });

});

🐇 Faster

🐞 Easier to debug

 📐 Edge case testing

🧰 Less setup

🖌 Unit Testing

🐁 Test a small piece of code

🎯 Precision

😊

😔

@yjaaidi

it('should parse price', () => {

  const price = parsePrice('30.01€');

  expect(price).toEqual({
    coefficient: 3001,
    exponent: -2,
    currency: 'EUR'
  });

});

🔗 More coupled to implementation

🤝 Doesn't test interactions

🐇 Faster

🐞 Easier to debug

 📐 Edge case testing

🧰 Less setup

Unit Tests are F.I.R.S.T.

@yjaaidi

🐇 Fast

🐻 Independent

🔁 Repeatable

🤖 Self-validating

⏰ Timely

@yjaaidi

Integration Tests

Like Unit Tests but...

🐢 Slow

🔗 Have an external dependency

or

or

🍕 Cover more than one unit of code

Integration Tests

@yjaaidi

Like Unit Tests but...

🐢 Slow

🔗 Have an external dependency

or

or

🍕 Cover more than one unit of code

What's the right size for a unit of code?

🐁
Small unit

🎭
Less mocks

💵
More tests

🐞
Easy debug

🔗
Coupled to implementation

🤝
No interaction

🚫

@yjaaidi

🐁
Small unit

🎭
Less mocks

💵
More tests

🐘
Big unit

🐞
Easy debug

🔗
Coupled to implementation

🤝
No interaction

🚫

🤝
Interaction

⛵️
Loosely coupled

🐞
Hard debug

💵
Less tests

🎭
More mocks

@yjaaidi

Classicists
Detroit Style

🐘 Bigger units

📉 Less tests for same coverage

⛵️ Testing the feature
(state-based testing)

🎭 Avoid mocks

🚫

@yjaaidi

Mockists
London Style

🐁 Small units

🎭 Mocks

📈 More tests for same coverage

🔗 Testing the implementation (interaction-based testing)

@yjaaidi

Code

Interaction Point

Test Suite

Test

Stub / Fake / Spy / Mock

Coverage

Mockists

@yjaaidi

Code

Interaction Point

Test Suite

Test

Stub / Fake / Spy / Mock

Coverage

Mockists

Classicists

Step 1

@yjaaidi

Code

Interaction Point

Test Suite

Test

Stub / Fake / Spy / Mock

Coverage

Mockists

Classicists

Step 1

Step 2

@yjaaidi

The Right Size for a Unit of Code

📈

Calculate your Return on Investment

@yjaaidi

The Right Size for a Unit of Code

🐘 Largest unit where the following conditions are still true

🎭 You don't need mocks (or not too many)

⏱ You can investigate broken tests in seconds

🏁 You can cover the whole unit

Test-Driven Development


Write tests first

@yjaaidi

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

@yjaaidi

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🏁
Know when you are done

@yjaaidi

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

@yjaaidi

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

@yjaaidi

📉
Less code

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🎯
Share intention

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🎯
Share intention

🔁
Early feedback

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🛀
Relaxed implementation

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🎯
Share intention

🔁
Early feedback

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🛀
Relaxed implementation

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🎯
Share intention

🔁
Early feedback

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🛀
Relaxed implementation

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🎯
Share intention

🔁
Early feedback

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🛀
Relaxed implementation

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🎯
Share intention

🔁
Early feedback

🔗
Not coupled
to implementation

🚫

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

Test-Driven Development


Write tests first

👷‍♂️
Focus on the feature

🛀
Relaxed implementation

🏁
Know when you are done

🏭
Avoid Artificial

Complexity

🎯
Share intention

🔁
Early feedback

🔗
Not coupled
to implementation

🚫

💉
TDD
Addiction

@yjaaidi

📉
Less code

💵
Cheap
Refactoring

TDD Limitations

@yjaaidi

🙍🏻‍♂️ Doesn't enforce collective ownership

🐘 Doesn't encourage small changes

🦋 Doesn't solve asymmetry

🚫

Limbo

while(true);
do
  git pull --rebase;
  git push;
done;

🦋 More symmetry

🍝 Less git spaghetti (cherry pick, rebase, squash, ...)

🤬 Less conflict

Timeboxed TDD

@yjaaidi

Reset timer

⌛️

Timeout

🗜

Test

⌨️

Code

OK

Commit
& Push

🔥

Revert

🚨

Fail

Few
minutes

Test && Commit || Revert

@yjaaidi

Lars Barlindhaug

Kent Beck

Ole Tjensvoll​ Johannessen

initiated in Oslo 🇳🇴

by

@barlindh

@kentbeck

Oddmund Strømme

@jraregris

💡 An idea from

Test && Commit || Revert

@yjaaidi

🗜

Test

⌨️

Code

OK

Commit
& Push

🔥

Revert

🚨

Fail

@yjaaidi

Unit Testing

E2E Testing

Visual Testing

Karma

Jest

Protractor

Storybook

+

Jest

+

+

Let's buy sandwiches

@yjaaidi

@yjaaidi

<sandwich-list>
<cart-preview>
Cart

@yjaaidi

<sandwich-list>
<cart-preview>
Cart
totalPrice
addSandwich(sandwich)

@yjaaidi

⌨️ Grab your keyboards ⌨️

@yjaaidi

⌨️

⌨️

⌨️

⌨️

⌨️

⌨️

1. Limbo

while(true);
do
  git pull --rebase;
  git push;
done;

@yjaaidi

yarn test # or npm test

2. Run tests

@yjaaidi

describe('Cart', () => {

  xit('should add sandwich', () => {
    throw new Error('🚧 Work in progress');
  });

  xit('should get total price', () => {
    throw new Error('🚧 Work in progress');
  });

});

3. Empty specs

src/app/cart/cart.spec.ts

@yjaaidi

describe('Cart', () => {

  xit('should add sandwich', () => {
    throw new Error('🚧 Work in progress');
  });

  xit('should get total price', () => {
    throw new Error('🚧 Work in progress');
  });

});

3. Empty specs

src/app/cart/cart.spec.ts
git commit -m "🗜 Cart specs. 🎯 Add sandwich to cart."

@yjaaidi

  xit('should add sandwich', () => {
    // cart.addSandwich(a 10€ burger)
    // cart.addSandwich(a 5.3€ butter & butter)
    // get sandwishes list
    // check count & order
    throw new Error('🚧 Work in progress');
  });

4. Comment-Driven Development

git commit -m "🗜 Cart add sanwich specs. 🎯 Add sandwich to cart."

@yjaaidi

xit('should add sandwich', () => {

 const burger = new Sandwich({title: 'Burger', price: 10});
 const butter = new Sandwich({title: 'Butter & butter', price: 5.3});

 cart.addSandwich(burger);
 cart.addSandwich(butter);

 const sandwichList = cart.getSandwichList();

 expect(sandwichList).toEqual([burger, butter]);
    
});

5. Write the test

git commit -m "🗜 Cart add sanwich specs. 🎯 Add sandwich to cart."

@yjaaidi

xit('should add sandwich', () => {

 const burger = new Sandwich({title: 'Burger', price: 10});
 const butter = new Sandwich({title: 'Butter & butter', price: 5.3});

 cart.addSandwich(burger);
 cart.addSandwich(butter);

 const sandwichList = cart.getSandwichList();

 expect(sandwichList).toEqual([burger, butter]);
    
});

5. Write the test

@yjaaidi

class Cart {
  /**
   * @deprecated Work in progress
   */
  addSandwich(sandwich: Sandwich) {
    throw new Error('🚧 Work in progress');
  }
}

6. Generate code
& pre-deprecate

git commit -m "🗜 Cart add sanwich specs. 🎯 Add sandwich to cart."

@yjaaidi

it('should add sandwich', () => {
  ...
});

7. Enable test & make it pass

git commit -m "🚧 Add sanwich to cart. 🎯 Add sandwich to cart."

@yjaaidi

Recap

1. 💃 Limbo
​2. ♾ Run tests forever

3.   Empty specs

4. 📝 Comment-Driven Development

5. 🗜 Write the test

6. 💀 Generate skeleton & pre-deprecate

7. ⌨️ Enable test & make it pass

8. 🧹 Remove deprecation

@yjaaidi

Jest

@yjaaidi

Jest

⚡️ Fast

@yjaaidi

Jest

👌 Less configuration

⚡️ Fast

@yjaaidi

Jest

👌 Less configuration

✌️ Test parallelization & isolation

⚡️ Fast

@yjaaidi

Jest

👌 Less configuration

🚀 No browser

✌️ Test parallelization & isolation

⚡️ Fast

@yjaaidi

Jest

👌 Less configuration

🚀 No browser

✌️ Test parallelization & isolation

⚡️ Fast

✅ Readable reports

@yjaaidi

Jest

👌 Less configuration

🚀 No browser

✌️ Test parallelization & isolation

⚡️ Fast

✅ Readable reports

🏘 Big community

@yjaaidi

Jest

     Compatible with Jasmine syntax

👌 Less configuration

🚀 No browser

✌️ Test parallelization & isolation

⚡️ Fast

✅ Readable reports

🏘 Big community

@yjaaidi

Jest

@yjaaidi

npm install -g @briebug/jest-schematic

ng g @briebug/jest-schematic:add

yarn test --watch # or npm test -- --watch

Jest ♥ Angular

@yjaaidi

Testing Components

@yjaaidi

ItemList
ItemPreview
<div>
<item-preview>
Component
{
   ...
}
<div>
<span>
Component
{
   ...
}

@yjaaidi

<div>
<item-preview>
Component
{
   ...
}
<div>
<span>
Component
{
   ...
}

"Integration"

TestBed.configureTestingModule({
  imports: [ItemListModule]
});
ItemList
ItemPreview

@yjaaidi

<div>
<item-preview>
Component
{
   ...
}
<div>
<span>
Component
{
   ...
}

Shallow

"Integration"

TestBed.configureTestingModule({
  imports: [ItemListModule]
});
TestBed.configureTestingModule({
  declarations: [ItemListComponent],
  errors: [NO_ERRORS_SCHEMA]
});
ItemList
ItemPreview

@yjaaidi

<div>
<item-preview>
Component
{
   ...
}
<div>
<span>
Component
{
   ...
}

Isolated

Shallow

"Integration"

TestBed.configureTestingModule({
  imports: [ItemListModule]
});
TestBed.configureTestingModule({
  declarations: [ItemListComponent],
  errors: [NO_ERRORS_SCHEMA]
});
cmp = new ItemListComponent(...);
ItemList
ItemPreview

@yjaaidi

Mocking a Service

describe('SandwichListComponent', () => {

  let cart: Cart;
  beforeEach(() => cart = TestBed.get(Cart));
  
  it('should add sandwich', () => {
  
    spyOn(cart, 'addSandwich');
  
    clickFirstBuyButton();
   
    expect(cart.addSandwich).toHaveBeenCalledWith(burger);
  
  });

});

@yjaaidi

Dependency Injection Override

  beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [SandwichListComponent],
        providers: [
          {
            provide: Cart,
            useClass: FakeCart
          }
        ]
    })
        .compileComponents();
  }));

@yjaaidi

Next episodes...

✅ E2E testing

🐒 HttpTestingModule & RouterTestingModule

🦋 Visual testing

@yjaaidi

🏀 RxJS Marble Testing

Prochaine Formation

17 au 19 juin à Lyon

@yjaaidi

 Guide Angular 👀 

Angular Testing Workshop

By Younes

Angular Testing Workshop

  • 1,224