「JavaScriptQAエンジニア」コースの将来の学生とテスト自動化のトピックに関心のあるすべての人のために、役立つ記事の翻訳を用意しました。
また、「テスターがJSについて知っておくべきこと」というトピックに関する公開ウェビナーに参加することをお勧めします。レッスンでは、参加者は専門家と一緒に、テストを作成するときに留意する必要があるJSの機能について検討します。
ユニットテストは素晴らしいです...それらが確実に機能するとき!実際、「悪いテストはテストがまったくないよりも悪い」という古い格言があります。偶発的な「偽陰性」テストの追跡に費やした数週間は効果がないことを確認できます。代わりに、今回はユーザーを支援するための実用的なコードを書くために使用できます。
: .
, , , .
,
( )
.
?
— , . . , « », "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);
};
, , .
, .