Test Driven Development

Frontend

Possible or impossible?

Who Am I?

  • Full Stack Developer at Lean Mind
  • Passionate about development
  • Typescript lover ❤️

Adrián Ferrera González

@afergon

adrian-afergon

Lean Coders

This is a grandparent history

  • The good
  • The bad
  • The mistakes

What is TDD?

Classic way:

TDD way:

Why we use it?

FF -> Fast feedback

Hight code coverage

Ensure the features

Other way to document

I can do it on my way!!

  • I'm faster without TDD
  • Is imposible to test the unexistent
  • My client don't want it!!!
  • What time expend in find a bug?
  • Are you testing code or features?
  • Do you tell your surgeon how to do it?

Is necessary?

  • expect(true).toBeTruthy()
  • You don't trust in your libs

Propably you have lisened something like...

This is not about TDD,

is about the way you test

What is the purpouse?

This is not about create test

Is about develop based in use small use cases

Using the test as tools

Tests Types

  • e2e
  • Unitary
  • Integration

That I ussually use

Tests Types

  • e2e
  • Unitary
  • Integration

That I ussually use

Expects are use cases

Clone of real environment

Are to expensive

Real coverage

Tests Types

  • e2e
  • Unitary
  • Integration

That I ussually use

Expects are abstraction functionalities

No require specific configs

Are faster and cheaper

Local component coverage

Tests Types

  • e2e
  • Unitary
  • Integration

That I ussually use

Expects are abstraction functionalities

Require mocks to set limits

Perfect balance

Important coverage but not always real

Tests Types

  • e2e
  • Unitary
  • Integration

That I ussually use

IMPORTANT!!!

You need an architecture!!

References for Architecture

Books:

Videos:

Slides:

Before start

let's talk about concepts

The Approach

OUTSIDE-IN

INSIDE-OUT

Emerging architecture

Componentisation

Predefined architecture

Fuzzy components

Testign Concepts

Before test

Mock

Spy

Replace a real function or var, by other that we want

Lisent a value for make questions

With Jest:

const foo = jest.fn();
const message = 'Hello TLP Innova';

foo(message);

expect(foo).toHaveBeenCalled();
expect(foo).toHaveBeenCalledWith(message);
const spy = jest.spyOn(video, 'play', 'get'); // we pass 'get'
const isPlaying = video.play;

expect(spy).toHaveBeenCalled();
expect(isPlaying).toBe(true);

spy.mockRestore();

Mock a function or mock an object

Concepts

Before test

Black-box

SCHRÖDINGER CAT

Concepts

About React

React

Redux

Hooks

Library to create components

Pattern to handle the state

Functions to share logic

We have to mount it

We have to use as black-box

We have to test independently

Providers

React Tool to handle the context

REAL CASE

Define use cases

  • We want to pick a champion and see his stats and general info.

In main page

  • We display all available champions, when we pick one, he/she is added to the build, then we recieve a list of suggestions champs that match well with our team.

In team builder

In best builds

  • We want to display best ranked builds

Define use cases

Define use cases

This tests take a lot of time to be runned

So it is very important to be very specific

Conclusions

Emerged Design

How can we do it

Hight Order Components

TIP: The end goal is not to break the app

TIP: Don't rush through the code

# Help TIP 1:

describe('Champion items', () => {
    it('should be displayed', () => {









        expect(wrapper.exists()).toBeTruthy();
    });
});
describe('Champion items', () => {
    it('should be displayed', () => {









        expect(wrapper.exists()).toBeTruthy(); // what is wrapper?
    });
});
describe('Champion items', () => {
    it('should be displayed', () => {



        const wrapper = shallow(
            <ChampionItems 
                service={aService} 
                isLoading={aLoadState}
            />
        );
        expect(wrapper.exists()).toBeTruthy();
    });
});
describe('Champion items', () => {
    it('should be displayed', () => {



        const wrapper = shallow(
            <ChampionItems 
                service={aService} 
                isLoading={aLoadState}
            />
        ); // and this props? :( 
        expect(wrapper.exists()).toBeTruthy();
    });
});
describe('Champion items', () => {
    it('should be displayed', () => {

        const aLoadState = false;
        const aService = { getItems: () => Promise.resolve(someItems)}
        const wrapper = shallow(
            <ChampionItems 
                service={aService} 
                isLoading={aLoadState}
            />
        );
        expect(wrapper.exists()).toBeTruthy();
    });
});
describe('Champion items', () => {
    it('should be displayed', () => {
        const someItems = ['B.F. Sword', 'Recurve Bow', 'Spatula'];
        const aLoadState = false;
        const aService = { getItems: () => Promise.resolve(someItems)}
        const wrapper = shallow(
            <ChampionItems 
                service={aService} 
                isLoading={aLoadState}
            />
        );
        expect(wrapper.exists()).toBeTruthy();
    });
});

Go from expect to definition

TODO: introduce jest

Emerged Design

Custom tool to generate boilerplate

# Help TIP 2:

Remeber true === true? 

Emerged Design

import { shallow } from 'enzyme';
import * as React from 'react';
import { Image} from './';

describe('Image', ()=> {
  it('should display the component', ()=>{
    const wrapper = shallow(<Image />);
    expect(wrapper.exists()).toBeTruthy();
  });





});
import { shallow } from 'enzyme';
import * as React from 'react';
import { Image} from './';

describe('Image', ()=> {
  it('should display the component', ()=>{
    const wrapper = shallow(<Image />);
    expect(wrapper.exists()).toBeTruthy();
  });

  it('should display the image source', function () {
    const wrapper = shallow(<Image src="irrelevant source"/>);
    expect(wrapper.exists()).toBeTruthy();
  });
});
import { shallow } from 'enzyme';
import * as React from 'react';
import { Image} from './';

describe('Image', ()=> {
  it('should display the image source', function () {
    const wrapper = shallow(<Image src="irrelevant source"/>);
    expect(wrapper.exists()).toBeTruthy();
  });
});

Refactor

import * as React from 'react';
import './Image.scss';

type ImageProps = React.DetailedHTMLProps<
    React.ImgHTMLAttributes<HTMLImageElement>, 
    HTMLImageElement
>;

export const Image: React.FC<ImageProps> = (props) => (
  <img {...props} />
);

Image.displayName = 'Image';

Implement

import { shallow } from 'enzyme';
import * as React from 'react';
import { Image} from './';

describe('Image', ()=> {
  it('should display the component', ()=>{
    const wrapper = shallow(<Image />);
    expect(wrapper.exists()).toBeTruthy();
  });

  it('should display the image source', function () {
    const wrapper = shallow(<Image src="irrelevant source"/>);
    expect(wrapper.exists()).toBeTruthy();
  });
});

Emerged Design

import * as React from 'react';
import './ChampionImage.scss';
import { Image } from '../Image/';

type ChampionImageProps = {
    id:string, 
    name: string, 
    src: string, 
    onClick: (id: string) => void
};


export const ChampionImage: React.FC<ChampionImageProps> = 
({id, name, src, onClick}) => {

    const handleClick = () => {
        onClick(id)
    };

    return (
    <div onClick={handleClick}>
        <Image src={src} alt={name} data-test-id="image"/>
    </div>
)};

ChampionImage.displayName = 'ChampionImage';
  it('should find the image inside', () => {
    
        
             
        

    
    
    
  });

  it('should call an event when is clicked', () => {










  });
  it('should find the image inside', () => {
    const wrapper = shallow(
        <ChampionImage 
            src="irrelevant champion image" 
            name="Irrelevant champion name"
        />);
    expect(
        wrapper.find('[data-test-id="image"]').exists()
    ).toBeTruthy();
  });

  it('should call an event when is clicked', () => {










  });
  it('should find the image inside', () => {
    const wrapper = shallow(
        <ChampionImage 
            src="irrelevant champion image" 
            name="Irrelevant champion name"
        />);
    expect(
        wrapper.find('[data-test-id="image"]').exists()
    ).toBeTruthy();
  });

  it('should call an event when is clicked', () => {










  });
import * as React from 'react';
import './ChampionImage.scss';
import { Image } from '../Image/';

type ChampionImageProps = {
     
    name: string, 
    src: string, 

};

export const ChampionImage: React.FC<ChampionImageProps> = 
    ({name, src}) => {


    
    
    

    return (
    <div>
        <Image src={src} alt={name} data-test-id="image"/>
    </div>
)};

ChampionImage.displayName = 'ChampionImage';
  it('should find the image inside', () => {
    const wrapper = shallow(
        <ChampionImage 
            src="irrelevant champion image" 
            name="Irrelevant champion name"
        />);
    expect(
        wrapper.find('[data-test-id="image"]').exists()
    ).toBeTruthy();
  });

  it('should call an event when is clicked', () => {
    const aClickHandler = jest.fn();
    const wrapper = shallow(
        <ChampionImage
            id="irrelevant id"
            src="irrelevant champion image"
            name="Irrelevant champion name"
            onClick={aClickHandler}
        />);
    wrapper.simulate('click');
    expect(aClickHandler).toHaveBeenCalledWith("irrelevant id");
  });
import * as React from 'react';
import './ChampionImage.scss';
import { Image } from '../Image/';

type ChampionImageProps = {
    id?:string, 
    name: string, 
    src: string, 
    onClick?: (id: string) => void
};

export const ChampionImage: React.FC<ChampionImageProps> = 
    ({id, name, src, onClick}) => {
    const handleClick = () => {
        if(onClick && id) {
            onClick(id)
        }
    };

    return (
    <div onClick={handleClick}>
        <Image src={src} alt={name} data-test-id="image"/>
    </div>
)};

ChampionImage.displayName = 'ChampionImage';
  it('should find the image inside', () => {
    const wrapper = shallow(
        <ChampionImage 
            src="irrelevant champion image" 
            name="Irrelevant champion name"
        />);
    expect(
        wrapper.find('[data-test-id="image"]').exists()
    ).toBeTruthy();
  });

  it('should call an event when is clicked', () => {
    const aClickHandler = jest.fn();
    const wrapper = shallow(
        <ChampionImage
            id="irrelevant id"
            src="irrelevant champion image"
            name="Irrelevant champion name"
            onClick={aClickHandler}
        />);
    wrapper.simulate('click');
    expect(aClickHandler).toHaveBeenCalledWith("irrelevant id");
  });

Implement

x2

Emerged Design

Take care with compilation errors

# Help TIP 3:

Emerged Design

When we pass the test, we can refactor this code

type ChampionId = string;

interface Champion {
    id: ChampionId,
    name: string,
    image: string
}
let aChampion: Champion;
beforeEach(() => {
    aChampion = {
      id: "Irrelevant champion id",
      name: "Irrelevant champion name",
      image: "Irrelevant champion image"
    }
  });
type ChampionImageProps = {
    id?:string,
    name: string,
    src: string,
    onClick?: (id: string) => void
    champion?: Champion;
}
<div onClick={handleClick}>
    <Image src={src} alt={name} data-test-id="image"/>
</div>
<div onClick={handleClick}>
    <Image src={champion.image} alt={champion.name} data-test-id="image"/>
</div>
  it('should find the image inside', () => {
    const wrapper = shallow(
        <ChampionImage 
            src="irrelevant champion image" 
            name="Irrelevant champion name"

        />);
    expect(
        wrapper.find('[data-test-id="image"]').exists()
    ).toBeTruthy();
  });

  it('should call an event when is clicked', () => {
    const aClickHandler = jest.fn();
    const wrapper = shallow(
        <ChampionImage
            id="irrelevant id"
            src="irrelevant champion image"
            name="Irrelevant champion name"

            onClick={aClickHandler}
        />);
    wrapper.simulate('click');
    expect(aClickHandler).toHaveBeenCalledWith("irrelevant id");
  });
  it('should find the image inside', () => {
    const wrapper = shallow(
        <ChampionImage 
            src="irrelevant champion image" 
            name="Irrelevant champion name"
            champion={aChampion}
        />);
    expect(
        wrapper.find('[data-test-id="image"]').exists()
    ).toBeTruthy();
  });

  it('should call an event when is clicked', () => {
    const aClickHandler = jest.fn();
    const wrapper = shallow(
        <ChampionImage
            id="irrelevant id"
            src="irrelevant champion image"
            name="Irrelevant champion name"
            champion={aChampion}
            onClick={aClickHandler}
        />);
    wrapper.simulate('click');
    expect(aClickHandler).toHaveBeenCalledWith("irrelevant id");
  });
  it('should find the image inside', () => {
    const wrapper = shallow(
        <ChampionImage 


            champion={aChampion}
        />);
    expect(
        wrapper.find('[data-test-id="image"]').exists()
    ).toBeTruthy();
  });

  it('should call an event when is clicked', () => {
    const aClickHandler = jest.fn();
    const wrapper = shallow(
        <ChampionImage



            champion={aChampion}
            onClick={aClickHandler}
        />);
    wrapper.simulate('click');
    expect(aClickHandler).toHaveBeenCalledWith("irrelevant id");
  });

Paralel change!!

Emerged Design

Now let's create a Champion selector

import { shallow } from 'enzyme';
import * as React from 'react';
import { ChampionSelector} from './';

describe('ChampionSelector', ()=> {
  it('should display the component', ()=>{
    const wrapper = shallow(<ChampionSelector />);
    expect(wrapper.exists()).toBeTruthy();
  })
});

champions

import { shallow } from 'enzyme';
import * as React from 'react';
import { ChampionSelector} from './';
import {Champion} from "../ChampionImage/ChampionImage.spec";

describe('ChampionSelector', ()=> {
  it('should display the champions', ()=>{
    const someChampions: Champion[] = [];
    const wrapper = shallow(<ChampionSelector champions={someChampions}/>);
    const championImages = wrapper.find('ChampionImage');
    expect(championImages.length).toBe(someChampions.length);
  })
});
import { shallow } from 'enzyme';
import * as React from 'react';
import { ChampionSelector} from './';
import {Champion} from "../ChampionImage/ChampionImage.spec";

describe('ChampionSelector', ()=> {
  it('should display the champions', ()=>{
    const someChampions: Champion[] = [aatrox, kayle, fiora];
    const wrapper = shallow(<ChampionSelector champions={someChampions}/>);
    const championImages = wrapper.find('ChampionImage');
    expect(championImages.length).toBe(someChampions.length);
  })
});

Emerged Design

Building helpers

import { shallow } from 'enzyme';
import * as React from 'react';
import { ChampionSelector} from './';
import {Champion} from "../ChampionImage/ChampionImage.spec";

describe('ChampionSelector', ()=> {
  it('should display the champions', ()=>{
    const someChampions: Champion[] = [aatrox, kayle, fiora];
    const wrapper = shallow(<ChampionSelector champions={someChampions}/>);
    const championImages = wrapper.find('ChampionImage');
    expect(championImages.length).toBe(someChampions.length);
  })
});
const aatrox: Champion = {};
const kayle: Champion = {};
const fiora: Champion = {};
const someChampions: Champion[] = [aatrox, kayle, fiora];
const buildChampion = ({id, name, image}: Champion):Champion => ({
  id: id ? id : 'irrelevant id',
  name: name ? name : 'irrelevant name',
  image: image ? image : 'irrelevant image'
});
it('should display the champions', ()=>{
    const aatrox: Champion = buildChampion({name: 'Aatrox'} as Champion);
    const kayle: Champion = buildChampion({name: 'Kayle'} as Champion);
    const fiora: Champion = buildChampion({name: 'Fiora'} as Champion);
    const someChampions: Champion[] = [atrox, kayle, fiora];
    const wrapper = shallow(<ChampionSelector champions={someChampions}/>);
    const championImages = wrapper.find('ChampionImage');
    expect(championImages.length).toBe(someChampions.length);
});

# Help TIP 4:

Emerged Design

Now we should implement for green

import * as React from 'react';
import './ChampionSelector.scss';
import {Champion} from "../ChampionImage/ChampionImage.spec";
import {ChampionImage} from "../ChampionImage";

interface ChampionSelectorProps {
    champions: Champion[];
}

export const ChampionSelector: React.FC<ChampionSelectorProps> 
    = ({champions}) =>
    champions.map((champion) => 
        <ChampionImage
            key={champion.id}
            champion={champion}
                
                                        />
    );

ChampionSelector.displayName = 'ChampionSelector';
it('should display the champions', ()=>{
    const aatrox: Champion = buildChampion({name: 'Aatrox'} as Champion);
    const kayle: Champion = buildChampion({name: 'Kayle'} as Champion);
    const fiora: Champion = buildChampion({name: 'Fiora'} as Champion);
    const someChampions: Champion[] = [atrox, kayle, fiora];
    const wrapper = shallow(<ChampionSelector champions={someChampions}/>);
    const championImages = wrapper.find('ChampionImage');
    expect(championImages.length).toBe(someChampions.length);
});

Implement

it('should select a champion when is clicked', () => {
   const aSelectHandler = jest.fn();
   const aatrox: Champion = buildChampion({name: 'Aatrox'} as Champion);
   const kayle: Champion = buildChampion({name: 'Kayle'} as Champion);
   const fiora: Champion = buildChampion({name: 'Fiora'} as Champion);
   const someChampions: Champion[] = [aatrox, kayle, fiora];
   const wrapper = shallow(
     <ChampionSelector 
         champions={someChampions} 
         onSelect={aSelectHandler}
     />);
   const aatroxImage = wrapper.find('[data-test-id]="Aatrox"');
   aatroxImage.simulate('click');
   expect(aSelectHandler).toHaveBeenCalled();
});
it('should select a champion when is clicked', () => {
   const aSelectHandler = jest.fn();
   const aatrox: Champion = buildChampion({name: 'Aatrox'} as Champion);
   const kayle: Champion = buildChampion({name: 'Kayle'} as Champion);
   const fiora: Champion = buildChampion({name: 'Fiora'} as Champion);
   const someChampions: Champion[] = [aatrox, kayle, fiora];
   const wrapper = shallow(
     <ChampionSelector 
         champions={someChampions} 
         onSelect={aSelectHandler}
     />);
   const aatroxImage = wrapper.find('[data-test-id]="Aatrox"');
   aatroxImage.simulate('click');
   expect(aSelectHandler).toHaveBeenCalled();
});

Implement

import * as React from 'react';
import './ChampionSelector.scss';
import {Champion} from "../ChampionImage/ChampionImage.spec";
import {ChampionImage} from "../ChampionImage";

interface ChampionSelectorProps {
    champions: Champion[];
}

export const ChampionSelector: React.FC<ChampionSelectorProps> 
    = ({champions}) =>
    champions.map((champion) => 
        <ChampionImage
            key={champion.id}
            champion={champion}
            onClick={onSelect}
            data-test-id={champion.name}/>
    );

ChampionSelector.displayName = 'ChampionSelector';

Emerged Design

describe('ChampionSelector', () => {
  const aatrox: Champion = buildChampion({name: 'Aatrox'} as Champion);
  const kayle: Champion = buildChampion({name: 'Kayle'} as Champion);
  const fiora: Champion = buildChampion({name: 'Fiora'} as Champion);
  const someChampions: Champion[] = [aatrox, kayle, fiora];

  let wrapper: ShallowWrapper;
  let aSelectHandler: jest.Mock;

  beforeEach(() => {
    aSelectHandler = jest.fn();
    wrapper = shallow(<ChampionSelector champions={someChampions} onSelect={aSelectHandler}/>);
  });

  it('should display the champions', () => {
    const championImages = wrapper.find('ChampionImage');

    expect(championImages.length).toBe(someChampions.length);
  });

  it('should select a champion when is clicked', () => {
    const aatroxImage = wrapper.find('[data-test-id="Aatrox"]');
    aatroxImage.simulate('click');

    expect(aSelectHandler).toHaveBeenCalled();
  });
});

const buildChampion = ({id, name, image}: Champion): Champion => ({
  id: id ? id : 'irrelevant id',
  name: name ? name : 'irrelevant name',
  image: image ? image : 'irrelevant image',
});

Define constants

Emerged Design

Champion Selector:

Image

Champion Image

Champion Selector

Emerged Design

Conclusions

  • All time functional code
  • We never have over ingeniering

Black box

The containers are a good element to test as a black box

Black box

Let's create a containter and check what it has to do

import { mount } from 'enzyme';
import * as React from 'react';
import { ChampionsInfo } from './';

describe('ChampionsInfo', () => {

  let championService: ChampionService;

  beforeEach(() => {
    championService = new ChampionService();
  });

  it('should load a champions list when ' +
    'the component is displayed', () => {
    
    championService.getChampions = jest.fn(() => 
        Promise.resolve(someChampions)
    );

    const wrapper = mount(
        <ChampionsInfo 
            championService={championService}
        />);
    wrapper.update();

    expect(championService.getChampions).toHaveBeenCalled();
  });
});
import * as React from 'react';
import './ChampionsInfo.scss';
import {useEffect, useState} from 'react';

export const ChampionsInfo: React.FC<{
    championService: ChampionService 
}> = ({championService}) => {

    const [champions, setChampions] = useState([]);
    useEffect(() => {
        championService
            .getChampions()
            .then(setChampions);
    }, []);

    return (
    <div className="ChampionsInfo">
      Hello from ChampionsInfo!
    </div>
  );
};

ChampionsInfo.displayName = 'ChampionsInfo';

Implement

Black box

Evolution Problem

React Hooks

React

evolves

Enzyme

React-testing-library

We make a mistake no wrapping our libs

Black box

export interface ChampionService {
  getChampions: () => Promise<Champion[]>;
}

describe('ChampionsInfo', () => {
  const aatrox: Champion = { info: '', image: '', id: 'irrelevant 1', name: 'aatrox' };
  const kayle: Champion = { info: '', image: '', id: 'irrelevant 2', name: 'kayle' };
  const fiora: Champion = { info: '', image: '', id: 'irrelevant 3', name: 'fiora' };
  const someChampions: Champion[] = [aatrox, kayle, fiora];
  let championService: ChampionService;

  beforeEach(() => {
    championService = {
      getChampions: jest.fn(async () => someChampions),
    };
  });

  it('should load a champions list when the component is displayed', async () => {
    const { container } = await render(
      <ChampionsInfo championService={championService} />,
    );
    const championSelector = container.children[0];
    const section = championSelector.children[0];
    const displayedChampions = section.children;

    expect(championService.getChampions).toHaveBeenCalled();
    expect(displayedChampions.length).toEqual(someChampions.length);
  });
});
interface ChampionsInfoProps {
  championService: ChampionService;
}

export const ChampionsInfo: React.FC<ChampionsInfoProps> = 
  ({ championService }) => {
  const [champions, setChampions] = useState([] as Champion[]);
  useEffect(() => {
    championService.getChampions().then(setChampions);
  }, [championService]);

  return (
    <div className="ChampionsInfo">
      <ChampionSelector champions={champions} onSelect={() => ''} />
    </div>
  );
};

ChampionsInfo.displayName = 'ChampionsInfo';

Implement

export interface ChampionService {
  getChampions: () => Promise<Champion[]>;
}

describe('ChampionsInfo', () => {
  const aatrox: Champion = { info: '', image: '', id: 'irrelevant 1', name: 'aatrox' };
  const kayle: Champion = { info: '', image: '', id: 'irrelevant 2', name: 'kayle' };
  const fiora: Champion = { info: '', image: '', id: 'irrelevant 3', name: 'fiora' };
  const someChampions: Champion[] = [aatrox, kayle, fiora];
  let championService: ChampionService;

  beforeEach(() => {
    championService = {
      getChampions: jest.fn(async () => someChampions),
    };
  });

  it('should load a champions list when the component is displayed', async () => {
    const { container } = await render(
      <ChampionsInfo championService={championService} />,
    );
    const championSelector = container.children[0];
    const section = championSelector.children[0];
    const displayedChampions = section.children;

    expect(championService.getChampions).toHaveBeenCalled();
    expect(displayedChampions.length).toEqual(someChampions.length);
  });
});

Black box

it('should load a champions list when the component is displayed', async () => { ... });

it('should select a champion and display the info', () => {
    const { container } = await render(
      <ChampionsInfo championService={championService} />,
    );
    const aatroxImage = await findByTestId(container, 'aatrox-image');
    fireEvent.click(aatroxImage);
    const info = await findByTestId(container, 'info');
    expect(info.textContent).toEqual(aatrox.info);
});

Implement

export const ChampionsInfo: React.FC<ChampionsInfoProps> = ({ championService }) => {
  const [champions, setChampions] = useState([] as Champion[]);
  useEffect(() => {
    championService.getChampions().then(setChampions);
  }, [championService]);








  return (
    <div className="ChampionsInfo">
      <ChampionSelector champions={champions} onSelect={() => {}} />

    </div>
  );
};
it('should load a champions list when the component is displayed', async () => { ... });

it('should select a champion and display the info', async () => {
    const { container } = await render(
      <ChampionsInfo championService={championService} />,
    );
    const aatroxImage = await findByTestId(container, 'aatrox-image');
    fireEvent.click(aatroxImage);
    const info = await findByTestId(container, 'info');
    expect(info.textContent).toEqual(aatrox.info);
});
export const ChampionsInfo: React.FC<ChampionsInfoProps> = ({ championService }) => {
  const [champions, setChampions] = useState([] as Champion[]);
  useEffect(() => {
    championService.getChampions().then(setChampions);
  }, [championService]);

  const [selectedChampion, setSelectedChampion] = useState({} as Champion);

  const selectChampion = (championId: ChampionId) => {
    const foundChampion = champions.find((champion) => championId === champion.id);
    setSelectedChampion(foundChampion ? foundChampion : ({} as Champion));
  };

  return (
    <div className="ChampionsInfo">
      <ChampionSelector champions={champions} onSelect={selectChampion} />
      {selectedChampion && <ChampionCard champion={selectedChampion} />}
    </div>
  );
};

Black box

And if we use Redux or Providers?

it('should load a champions list when the component is displayed', async () => { ... });

it('should select a champion and display the info', () => {

    const { container } = await render(
      
      <ChampionsInfo championService={championService} />,

    );
    const aatroxImage = await findByTestId(container, 'aatrox-image');
    fireEvent.click(aatroxImage);
    const info = await findByTestId(container, 'info');
    expect(info.textContent).toEqual(aatrox.info);
});
it('should load a champions list when the component is displayed', async () => { ... });

it('should select a champion and display the info', () => {
    const stote = createAMockStore(championService);
    const { container } = await render(
      <Provider store={store}>
        <ChampionsInfoConnected />
      </Provider>,
    );
    const aatroxImage = await findByTestId(container, 'aatrox-image');
    fireEvent.click(aatroxImage);
    const info = await findByTestId(container, 'info');
    expect(info.textContent).toEqual(aatrox.info);
});

Create builder with services and state

Connect to redux

Wrapp the element

Black box

What happen with bugs?

If we found a bug when we are coding

We should create a new cases reproduce it

And then fix it

Black box

Conclusions

  • Postpone decisions
  • Economic tests for real cases
  • Check the integration

Posible or Imposible?

Conclusions

  • It's posible
  • Is efficent
  • Powerfull tool
  • Easy for other developers

Positive

Negative

  • Require knowledge
  • Require practice

If you love code,

this is for you

If you hate practice...

this will be difficult to you

Questions?

Thank you!!

Made with Slides.com