Für zukünftige Studenten des Kurses "JavaScript QA Engineer" und alle, die sich für das Thema Testautomatisierung interessieren, haben wir eine Übersetzung eines nützlichen Artikels vorbereitet.
Wir laden Sie außerdem ein, an dem offenen Webinar zum Thema "Was ein Tester über JS wissen muss" teilzunehmen . In der Lektion werden die Teilnehmer zusammen mit einem Experten die Funktionen von JS betrachten, die beim Schreiben von Tests berücksichtigt werden müssen.
Unit-Tests sind großartig ... wenn sie zuverlässig funktionieren! In der Tat gibt es ein altes Sprichwort, dass "ein schlechter Test schlimmer ist als überhaupt kein Test". Ich kann bestätigen, dass Wochen, die ich damit verbracht habe, einen versehentlichen "falsch negativen" Test zu verfolgen, nicht effektiv sind. Stattdessen könnte diese Zeit verwendet werden, um Arbeitscode zu schreiben, um dem Benutzer zu helfen.
: .
, , , .
,
( )
.
?
— , . . , « », "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);
};
, , .
, .