Não use acessórios em Cypress e testes de unidade - use funções de fábrica

Para futuros alunos do curso "JavaScript QA Engineer" e todos os interessados ​​no tópico de automação de testes, preparamos a tradução de um artigo útil.



Também o convidamos a participar do webinar aberto sobre o tópico "O que um testador precisa saber sobre JS" . Na lição, os participantes, junto com um especialista, irão considerar os recursos do JS que precisam ser mantidos em mente ao escrever testes.










Os testes de unidade são ótimos ... quando funcionam de forma confiável! Na verdade, existe um velho ditado que diz que "um teste ruim é pior do que nenhum teste". Posso confirmar que semanas passadas perseguindo um teste de "falso negativo" acidental não são eficazes. Em vez disso, esse tempo pode ser usado para escrever código de trabalho para ajudar o usuário.





: .





, , , .





,









  1. ( )









.





?

— , . . , « », "Gang Of Four's Design Pattern" . .





, , .





:





interface ISomeObj {
  percentage: string;
}

export const makeSomeObj = () => {
  return {
    percentage: Math.random()
  };
}
      
      



, , .





, , .





,

. , - . JSON-. Cypress ( ), JSON . , . JSON .





, . , , , .





// This file is "src/pages/newYorkInfo.tsx"
import * as React from 'react';

interface IUser {
    state: string;
    address: string;
    isAdmin: boolean;
    deleted: boolean | undefined;
}

export const NewYorkUserPage: React.FunctionComponent<{ user: IUser }> = props => {
    if (props.user.state === 'NY' && !props.user.deleted) {
        const welcomeMessage = `Welcome`;
        return <h1 id="ny-dashboard">{welcomeMessage}</h1>;
    } else {
        return <div>ACCESS DENIED</div>;
    }
};
      
      



, JSON .





// fixtures/user.json
{
    state: 'NY',
    isAdmin: true,
    address: '55 Main St',
}
      
      



. , - psuedo- Cypress, , , .





// When the UI calls the user endpoint, return the JSON as the mocked return value
cy.route('GET', '/user/**', 'fixture:user.json');
cy.visit('/dashboard');
cy.get('#ny-dashboard').should('exist')
      
      



, , . ?





— , JSON-

JSON- ? , , (). , JSON-. 52 JSON-, . , , 104 . !





. , Product Owner : « , ».





, name



.





// This file is "src/pages/newYorkInfo.tsx"
import * as React from 'react';

interface IUser {
    name: string;
    state: string;
    address: string;
    isAdmin: boolean;
    deleted: boolean | undefined;
}

export const NewYorkUserPage: React.FunctionComponent<{ user: IUser }> = props => {
    if (props.user.state === 'NY' && !props.user.deleted) {
        const welcomeMessage = `Welcome ${props.user.name.toLowerCase()}!`;
        return <h1 id="ny-dashboard">{welcomeMessage}</h1>;
    } else {
        return <div>ACCESS DENIED</div>;
    }
};
      
      



, , JSON . JSON name



, :





Uncaught TypeError: Cannot read property 'toLowerCase' of undefined
      
      



name 52 JSON . Typescript.





: TypeScript

JSON .ts , Typescript :





// this file is "testData/users"
import {IUser} from 'src/pages/newYorkInfo';

// Property 'name' is missing in type '{ state: string; isAdmin: true; address: string; deleted: false; }' but required in type 'IUser'.ts(2741)
export const generalUser: IUser = {
    state: 'NY',
    isAdmin: true,
    address: '55 Main St',
    deleted: false,
};
      
      



, .





import { generalUser } from 'testData/users';

// When the UI calls the user endpoint, return the JSON as the mocked return value
cy.route('GET', '/user/**', generalUser);
cy.visit('/dashboard');
cy.get('#ny-dashboard').should('exist')
      
      



Typescript! , name: 'Bob Smith'



GeneralUser:



, , , !





, . , .





, , , -. , , , , . deleted: false



generalUser



.





! , . .





( ) , . , ( ) deletedUser



, 1 . - — 5000 .





, .





// this file is "testData/users"
import {IUser} from 'src/pages/newYorkInfo';

export const nonAdminUser: IUser = {
    name: 'Bob',
    state: 'NY',
    isAdmin: false,
    address: '55 Main St',
    deleted: false,
};

export const adminUser: IUser = {
    name: 'Bob',
    state: 'NY',
    isAdmin: true,
    address: '55 Main St',
    deleted: false,
};

export const deletedAdminUser: IUser = {
    name: 'Bob',
    state: 'NY',
    isAdmin: true,
    address: '55 Main St',
    deleted: true,
};

export const deletedNonAdmin: IUser = {
    name: 'Bob',
    state: 'NY',
    isAdmin: false,
    address: '55 Main St',
    deleted: true,
};

// and on and on and on again...
      
      



.





:  

? !





// src/factories/user
import faker from 'faker';
import {IUser} from 'src/pages/newYorkInfo';

export const makeFakeUser = (): IUser => {
    return {
        name: faker.name.firstName() + ' ' + faker.name.lastName(),
        state: faker.address.stateAbbr(),
        isAdmin: faker.random.boolean(),
        address: faker.address.streetAddress(),
        deleted: faker.random.boolean(),
    }
}
      
      



makeFakeUser()



, .





, , , , . IUser, .





. , - . , .





import { makeFakeUser } from 'src/factories/user';
import {IUser} from 'src/pages/newYorkInfo';

// Arrange
const randomUser = makeFakeUser();
const deletedUser: IUser = { ...randomUser, ...{
  deleted: true
};
cy.route('GET', '/user/**', deletedUser);

// Act
cy.visit('/dashboard');

// Assert
cy.find('ACCESS DENIED').should('exist')
      
      



, , . , , , API , "Access Denied"



.





, .





: mergePartially

spread



, . , , :





interface IUser {
    userName: string;
    preferences: {
        lastUpdated?: Date;
        favoriteColor?: string;
        backupContact?: string;
        mailingAddress: {
            street: string;
            city: string;
            state: string;
            zipCode: string;
        }
     }
}
      
      



, .





, , DRY. , , , "Main Street".





const userOnMainSt = makeFakeUser({
    preferences: {
        mailingAddress: {
            street: 'Main Street'
        }
    }
});
      
      



, , , 7 . - . .





makeFakeUser







, mergePartially ( : mergePartially



).





const makeFakeUser = (override?: NestedPartial<IDeepObj>): IDeepObj => {
        const seed: IDeepObj = {
          userName: 'Bob Smith',
          preferences: {
            mailingAddress: {
              street: faker.address.streetAddress(),
              city: faker.address.city(),
              state: faker.address.stateAbbr(),
              zipCode: faker.address.zipCode(),
            },
          },
        };
        return mergePartially.deep(seed, override);
      };
      
      



, , .





, .






«JavaScript QA Engineer».









« JS ».












All Articles