溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

測試 Dojo 應(yīng)用程序(詳解)

發(fā)布時間:2020-07-20 10:10:43 來源:網(wǎng)絡(luò) 閱讀:181 作者:blocklang 欄目:web開發(fā)

測試服務(wù)

Intern 支持在 BrowserStack、SauceLabs 和 TestingBot 等服務(wù)上遠程運行測試。您可以選用其中一個服務(wù),注冊一個帳號并將憑據(jù)提供給 cli-test-intern。默認情況下,所有測試服務(wù)會在 IE11、Firefox 和 Chrome 等瀏覽器上運行測試。

BrowserStack

使用 BrowserStack 服務(wù),需提供 access key 和用戶名。Access key 和用戶名可在命令行中指定或設(shè)置為環(huán)境變量,詳見 Intern 文檔。

dojo test -a -c browserstack -k <accesskey> --userName <username>

或使用環(huán)境變量

BROWSERSTACK_USERNAME=<username> BROWSERSTACK_ACCESS_KEY=<key> dojo test -a -c browserstack

SauceLabs

使用 SauceLabs 服務(wù),需提供 access key 和用戶名。Access key 和用戶名可在命令行中指定或設(shè)置為環(huán)境變量,詳見 Intern 文檔。

dojo test -a -c saucelabs -k <accesskey> --userName <username>

或使用環(huán)境變量

SAUCE_USERNAME=<username> SAUCE_ACCESS_KEY=<key> dojo test -a -c saucelabs

TestingBot

使用 TestingBot 服務(wù),需提供 key 和 secret。Key 和 secret 可在命令行中指定或設(shè)置為環(huán)境變量,詳見 Intern 文檔。

dojo test -a -c testingbot -k <key> -s <secret>

或使用環(huán)境變量

TESTINGBOT_SECRET=<secret> TESTINGBOT_KEY=<key> dojo test -a -c saucelabs

harness

當(dāng)使用 @dojo/framework/testing 時,harness() 是最重要的 API,主要用于設(shè)置每一個測試并提供一個執(zhí)行虛擬 DOM 斷言和交互的上下文。目的在于當(dāng)更新 propertieschildren,以及部件失效時,鏡像部件的核心行為,并且不需要任何特殊或自定義邏輯。

API

interface HarnessOptions {
    customComparators?: CustomComparator[];
    middleware?: [MiddlewareResultFactory<any, any, any>, MiddlewareResultFactory<any, any, any>][];
}

harness(renderFunction: () => WNode, customComparators?: CustomComparator[]): Harness;
harness(renderFunction: () => WNode, options?: HarnessOptions): Harness;
  • renderFunction: 返回被測部件 WNode 的函數(shù)
  • customComparators: 一組自定義的比較器描述符。每個描述符提供一個比較器函數(shù),用于比較通過 selectorproperty 定位到的 properties
  • options: harness 的擴展選項,包括 customComparators 和一組 middleware/mocks 元組。

harness 函數(shù)返回一個 Harness 對象,該對象提供了幾個與被測部件交互的 API:

Harness

  • expect: 對被測部件完整的渲染結(jié)果執(zhí)行斷言
  • expectPartial: 對被測部件部分渲染結(jié)果執(zhí)行斷言
  • trigger: 用于在被測部件的節(jié)點上觸發(fā)函數(shù)
  • getRender: 根據(jù)提供的索引,從 harness 中返回對應(yīng)的渲染器

使用 @dojo/framework/core 中的 w() 函數(shù)生成一個用于測試的部件是非常簡單的:

const { describe, it } = intern.getInterface('bdd');
import WidgetBase from '@dojo/framework/widget-core/WidgetBase';
import harness from '@dojo/framework/testing/harness';
import { w, v } from '@dojo/framework/widget-core/d';

class MyWidget extends WidgetBase<{ foo: string }> {
    protected render() {
        const { foo } = this.properties;
        return v('div', { foo }, this.children);
    }
}

const h = harness(() => w(MyWidget, { foo: 'bar' }, ['child']));

如下所示,harness 函數(shù)也支持 tsx。README 文檔中其余示例均使用編程式的 w() API,在 單元測試 中可查看更多 tsx 示例。

const h = harness(() => <MyWidget foo="bar">child</MyWidget>);

renderFunction 是延遲執(zhí)行的,所以可在斷言之間包含額外的邏輯來操作部件的 propertieschildren。

describe('MyWidget', () => {
  it('renders with foo correctly', () => {
        let foo = 'bar';

        const h = harness(() => {
            return w(MyWidget, { foo }, [ 'child' ]));
        };

        h.expect(/** assertion that includes bar **/);
        // update the property that is passed to the widget
        foo = 'foo';
        h.expect(/** assertion that includes foo **/)
  });
});

Mocking 中間件

當(dāng)初始化 harness 時,可將 mock 中間件指定為 HarnessOptions 值的一部分。Mock 中間件被定義為由原始的中間件和 mock 中間件的實現(xiàn)組成的元組。Mock 中間件的創(chuàng)建方式與其他中間件相同。

import myMiddleware from './myMiddleware';
import myMockMiddleware from './myMockMiddleware';
import harness from '@dojo/framework/testing/harness';

import MyWidget from './MyWidget';

describe('MyWidget', () => {
    it('renders', () => {
        const h = harness(() => <MyWidget />, { middleware: [[myMiddleware, myMockMiddleware]] });
        h.expect(/** assertion that executes the mock middleware instead of the normal middleware **/);
    });
});

Harness 會自動 mock 很多核心中間件,并注入到任何需要他們的中間件中:

  • invalidator
  • setProperty
  • destroy
Dojo Mock 中間件

當(dāng)測試使用了 Dojo 中間件的部件時,有很多 mock 中間件可以使用。Mock 會導(dǎo)出一個 factory,該 factory 會創(chuàng)建一個受限作用域的 mock 中間件,會在每個測試中使用。

Mock node 中間件

使用 @dojo/framework/testing/mocks/middleware/node 中的 createNodeMock 可 mock 一個 node 中間件。要設(shè)置從 node mock 中返回的期望值,需要調(diào)用創(chuàng)建的 mock node 中間件,并傳入 key 和期望的 DOM node。

import createNodeMock from '@dojo/framework/testing/mocks/middleware/node';

// create the mock node middleware
const mockNode = createNodeMock();

// create a mock DOM node
const domNode = {};

// call the mock middleware with a key and the DOM
// to return.
mockNode('key', domNode);
Mock intersection 中間件

使用 @dojo/framework/testing/mocks/middleware/intersection 中的 createIntersectionMock 可 mock 一個 intersection 中間件。要設(shè)置從 intersection mock 中返回的期望值,需要調(diào)用創(chuàng)建的 mock intersection 中間件,并傳入 key 和期望的 intersection 詳情。

考慮以下部件:

import { create, tsx } from '@dojo/framework/core/vdom';
import intersection from '@dojo/framework/core/middleware/intersection';

const factory = create({ intersection });

const App = factory(({ middleware: { intersection } }) => {
    const details = intersection.get('root');
    return <div key="root">{JSON.stringify(details)}</div>;
});

使用 mock intersection 中間件:

import { tsx } from '@dojo/framework/core/vdom';
import createIntersectionMock from '@dojo/framework/testing/mocks/middleware/intersection';
import intersection from '@dojo/framework/core/middleware/intersection';
import harness from '@dojo/framework/testing/harness';

import MyWidget from './MyWidget';

describe('MyWidget', () => {
    it('test', () => {
        // create the intersection mock
        const intersectionMock = createIntersectionMock();
        // pass the intersection mock to the harness so it knows to
        // replace the original middleware
        const h = harness(() => <App key="app" />, { middleware: [[intersection, intersectionMock]] });

        // call harness.expect as usual, asserting the default response
        h.expect(() => <div key="root">{`{"intersectionRatio":0,"isIntersecting":false}`}</div>);

        // use the intersection mock to set the expected return
        // of the intersection middleware by key
        intersectionMock('root', { isIntersecting: true });

        // assert again with the updated expectation
        h.expect(() => <div key="root">{`{"isIntersecting": true }`}</div>);
    });
});
Mock resize 中間件

使用 @dojo/framework/testing/mocks/middleware/resize 中的 createResizeMock 可 mock 一個 resize 中間件。要設(shè)置從 resize mock 中返回的期望值,需要調(diào)用創(chuàng)建的 mock resize 中間件,并傳入 key 和期望的容納內(nèi)容的矩形區(qū)域。

const mockResize = createResizeMock();
mockResize('key', { width: 100 });

考慮以下部件:

import { create, tsx } from '@dojo/framework/core/vdom'
import resize from '@dojo/framework/core/middleware/resize'

const factory = create({ resize });

export const MyWidget = factory(function MyWidget({ middleware }) => {
    const  { resize } = middleware;
    const contentRects = resize.get('root');
    return <div key="root">{JSON.stringify(contentRects)}</div>;
});

使用 mock resize 中間件:

import { tsx } from '@dojo/framework/core/vdom';
import createResizeMock from '@dojo/framework/testing/mocks/middleware/resize';
import resize from '@dojo/framework/core/middleware/resize';
import harness from '@dojo/framework/testing/harness';

import MyWidget from './MyWidget';

describe('MyWidget', () => {
    it('test', () => {
        // create the resize mock
        const resizeMock = createResizeMock();
        // pass the resize mock to the harness so it knows to replace the original
        // middleware
        const h = harness(() => <App key="app" />, { middleware: [[resize, resizeMock]] });

        // call harness.expect as usual
        h.expect(() => <div key="root">null</div>);

        // use the resize mock to set the expected return of the resize middleware
        // by key
        resizeMock('root', { width: 100 });

        // assert again with the updated expectation
        h.expect(() => <div key="root">{`{"width":100}`}</div>);
    });
});
Mock Store 中間件

使用 @dojo/framework/testing/mocks/middleware/store 中的 createMockStoreMiddleware 可 mock 一個強類型的 store 中間件,也支持 mock process。為了 mock 一個 store 的 process,可傳入一個由原始 store process 和 stub process 組成的元組。中間件會改為調(diào)用 stub,而不是調(diào)用原始的 process。如果沒有傳入 stub,中間件將停止調(diào)用所有的 process。

要修改 mock store 中的值,需要調(diào)用 mockStore,并傳入一個返回一組 store 操作的函數(shù)。這將注入 store 的 path 函數(shù),以創(chuàng)建指向需要修改的狀態(tài)的指針。

mockStore((path) => [replace(path('details', { id: 'id' })]);

考慮以下部件:

src/MyWidget.tsx

import { create, tsx } from '@dojo/framework/core/vdom'
import { myProcess } from './processes';
import MyState from './interfaces';
// application store middleware typed with the state interface
// Example: `const store = createStoreMiddleware<MyState>();`
import store from './store';

const factory = create({ store }).properties<{ id: string }>();

export default factory(function MyWidget({ properties, middleware: store }) {
    const { id } = properties();
    const { path, get, executor } = store;
    const details = get(path('details');
    let isLoading = get(path('isLoading'));

    if ((!details || details.id !== id) && !isLoading) {
        executor(myProcess)({ id });
        isLoading = true;
    }

    if (isLoading) {
        return <Loading />;
    }

    return <ShowDetails {...details} />;
});

使用 mock store 中間件:

tests/unit/MyWidget.tsx

import { tsx } from '@dojo/framework/core/vdom'
import createMockStoreMiddleware from '@dojo/framework/testing/mocks/middleware/store';
import harness from '@dojo/framework/testing/harness';

import { myProcess } from './processes';
import MyWidget from './MyWidget';
import MyState from './interfaces';
import store from './store';

// import a stub/mock lib, doesn't have to be sinon
import { stub } from 'sinon';

describe('MyWidget', () => {
     it('test', () => {
          const properties = {
               id: 'id'
          };
         const myProcessStub = stub();
         // type safe mock store middleware
         // pass through an array of tuples `[originalProcess, stub]` for mocked processes
         // calls to processes not stubbed/mocked get ignored
         const mockStore = createMockStoreMiddleware<MyState>([[myProcess, myProcessStub]]);
         const h = harness(() => <MyWidget {...properties} />, {
             middleware: [store, mockStore]
         });
         h.expect(/* assertion template for `Loading`*/);

         // assert again the stubbed process
         expect(myProcessStub.calledWith({ id: 'id' })).toBeTruthy();

         mockStore((path) => [replace(path('isLoading', true)]);
         h.expect(/* assertion template for `Loading`*/);
         expect(myProcessStub.calledOnce()).toBeTruthy();

         // use the mock store to apply operations to the store
         mockStore((path) => [replace(path('details', { id: 'id' })]);
         mockStore((path) => [replace(path('isLoading', true)]);

         h.expect(/* assertion template for `ShowDetails`*/);

         properties.id = 'other';
         h.expect(/* assertion template for `Loading`*/);
         expect(myProcessStub.calledTwice()).toBeTruthy();
         expect(myProcessStub.secondCall.calledWith({ id: 'other' })).toBeTruthy();
         mockStore((path) => [replace(path('details', { id: 'other' })]);
         h.expect(/* assertion template for `ShowDetails`*/);
     });
});

Custom Comparators

在某些情況下,我們在測試期間無法得知屬性的確切值,所以需要使用自定義比較描述符(custom compare descriptor)。

描述符中有一個用于定位要檢查的虛擬節(jié)點的 selector,一個應(yīng)用自定義比較的屬性名和一個接收實際值并返回一個 boolean 類型斷言結(jié)果的比較器函數(shù)。

const compareId = {
    selector: '*', // all nodes
    property: 'id',
    comparator: (value: any) => typeof value === 'string' // checks the property value is a string
};

const h = harness(() => w(MyWidget, {}), [compareId]);

對于所有的斷言,返回的 harness API 將只對 id 屬性使用 comparator 進行測試,而不是標(biāo)準(zhǔn)的相等測試。

selectors

harness API 支持 CSS style 選擇器概念,來定位要斷言和操作的虛擬 DOM 中的節(jié)點。查看支持的選擇器的完整列表以了解更多信息。

除了標(biāo)準(zhǔn) API 之外還提供:

  • 支持將定位節(jié)點 key 屬性簡寫為 @ 符號
  • 當(dāng)使用標(biāo)準(zhǔn)的 . 來定位樣式類時,使用 classes 屬性而不是 class 屬性

harness.expect

測試中最常見的需求是斷言部件的 render 函數(shù)的輸出結(jié)構(gòu)。expect 接收一個返回被測部件期望的渲染結(jié)果的函數(shù)作為參數(shù)。

API

expect(expectedRenderFunction: () => DNode | DNode[], actualRenderFunction?: () => DNode | DNode[]);
  • expectedRenderFunction: 返回查詢節(jié)點期望的 DNode 結(jié)構(gòu)的函數(shù)
  • actualRenderFunction: 一個可選函數(shù),返回被斷言的實際 DNode 結(jié)構(gòu)
h.expect(() =>
    v('div', { key: 'foo' }, [w(Widget, { key: 'child-widget' }), 'text node', v('span', { classes: ['class'] })])
);

expect 也可以接收第二個可選參數(shù),返回要斷言的渲染結(jié)果的函數(shù)。

h.expect(() => v('div', { key: 'foo' }), () => v('div', { key: 'foo' }));

如果實際的渲染輸出和期望的渲染輸出不同,就會拋出一個異常,并使用結(jié)構(gòu)化的可視方法,用 (A) (實際值)和 (E) (期望值)指出所有不同點。

Example assertion failure output:

v('div', {
    'classes': [
        'root',
(A)     'other'
(E)     'another'
    ],
    'onclick': 'function'
}, [
    v('span', {
        'classes': 'span',
        'id': 'random-id',
        'key': 'label',
        'onclick': 'function',
        'style': 'width: 100px'
    }, [
        'hello 0'
    ])
    w(ChildWidget, {
        'id': 'random-id',
        'key': 'widget'
    })
    w('registry-item', {
        'id': true,
        'key': 'registry'
    })
])
harness.trigger

harness.trigger()selector 定位的節(jié)點上調(diào)用 name 指定的函數(shù)。

interface FunctionalSelector {
    (node: VNode | WNode): undefined | Function;
}

trigger(selector: string, functionSelector: string | FunctionalSelector, ...args: any[]): any;
  • selector: 用于查找目標(biāo)節(jié)點的選擇器
  • functionSelector: 要么是從節(jié)點的屬性中找到的被調(diào)用的函數(shù)名,或者是從節(jié)點的屬性中返回一個函數(shù)的函數(shù)選擇器
  • args: 為定位到的函數(shù)傳入的參數(shù)

如果有返回結(jié)果,則返回的是被觸發(fā)函數(shù)的結(jié)果。

用法示例:

// calls the `onclick` function on the first node with a key of `foo`
h.trigger('@foo', 'onclick');
// calls the `customFunction` function on the first node with a key of `bar` with an argument of `100`
// and receives the result of the triggered function
const result = h.trigger('@bar', 'customFunction', 100);

functionalSelector 返回部件屬性中的函數(shù)。函數(shù)也會被觸發(fā),與使用普通字符串 functionSelector 的方式相同。

用法示例:

假定有如下 VDOM 樹結(jié)構(gòu):

v(Toolbar, {
    key: 'toolbar',
    buttons: [
        {
            icon: 'save',
            onClick: () => this._onSave()
        },
        {
            icon: 'cancel',
            onClick: () => this._onCancel()
        }
    ]
});

并且你想觸發(fā) save 按鈕的 onClick 函數(shù)。

h.trigger('@buttons', (renderResult: DNode<Toolbar>) => {
    return renderResult.properties.buttons[0].onClick;
});

注意: 如果沒能找到指定的選擇器,則 trigger 會拋出一個錯誤。

harness.getRender

harness.getRender() 返回索引指定的渲染器,如果沒有提供索引則返回最后一個渲染器。

getRender(index?: number);
  • index: 要返回的渲染器的索引

用法示例:

// Returns the result of the last render
const render = h.getRender();
// Returns the result of the render for the index provided
h.getRender(1);

Assertion Templates

斷言模板(assertion template)允許你構(gòu)建期望的渲染函數(shù),用于傳入 h.expect() 中。斷言模板背后的思想來自經(jīng)常要斷言整個渲染輸出,并需要修改斷言的某些部分。

要使用斷言模板,需要先導(dǎo)入模塊:

import assertionTemplate from '@dojo/framework/testing/assertionTemplate';

然后,在你的測試中,你可以編寫一個基本斷言,它是部件的默認渲染狀態(tài):

假定有以下部件:

src/widgets/Profile.ts

import WidgetBase from '@dojo/framework/widget-core/WidgetBase';
import { v } from '@dojo/framework/widget-core/d';

import * as css from './styles/Profile.m.css';

export interface ProfileProperties {
    username?: string;
}

export default class Profile extends WidgetBase<ProfileProperties> {
    protected render() {
        const { username } = this.properties;
        return v('h2', { classes: [css.root] }, [`Welcome ${username || 'Stranger'}!`]);
    }
}

基本斷言如下所示:

tests/unit/widgets/Profile.ts

const { describe, it } = intern.getInterface('bdd');
import harness from '@dojo/framework/testing/harness';
import assertionTemplate from '@dojo/framework/testing/assertionTemplate';
import { w, v } from '@dojo/framework/widget-core/d';

import Profile from '../../../src/widgets/Profile';
import * as css from '../../../src/widgets/styles/Profile.m.css';

const profileAssertion = assertionTemplate(() =>
    v('h2', { classes: [css.root], '~key': 'welcome' }, ['Welcome Stranger!'])
);

在測試中這樣寫:

tests/unit/widgets/Profile.ts

const profileAssertion = assertionTemplate(() =>
    v('h2', { classes: [css.root], '~key': 'welcome' }, ['Welcome Stranger!'])
);

describe('Profile', () => {
    it('default renders correctly', () => {
        const h = harness(() => w(Profile, {}));
        h.expect(profileAssertion);
    });
});
it('default renders correctly', () => {
    const h = harness(() => w(Profile, {}));
    h.expect(profileAssertion);
});

現(xiàn)在我們看看,為 Profile 部件傳入 username 屬性后,如何測試輸出結(jié)果:

tests/unit/widgets/Profile.ts

describe('Profile', () => {
    ...

  it('renders given username correctly', () => {
    // update the expected result with a given username
    const namedAssertion = profileAssertion.setChildren('~welcome', [
      'Welcome Kel Varnsen!'
    ]);
    const h = harness(() => w(Profile, { username: 'Kel Varnsen' }));
    h.expect(namedAssertion);
  });
});

這里,我們使用 baseAssertion 的 setChildren() api,然后我們使用特殊的 ~ 選擇器來定位 key 值為 ~message 的節(jié)點。~key 屬性(使用 tsx 的模板中是 assertion-key)是斷言模板的一個特殊屬性,在斷言時會被刪除,因此在匹配渲染結(jié)構(gòu)時不會顯示出來。此功能允許你修飾斷言模板,以便能簡單的選擇節(jié)點,而不需要擴展實際的部件渲染函數(shù)。一旦我們獲取到 message 節(jié)點,我們就可以將其子節(jié)點設(shè)置為期望的 the number 5,然后在 h.expect 中使用生成的模板。需要注意的是,斷言模板在設(shè)置值時總是返回一個新的斷言模板,這可以確保你不會意外修改現(xiàn)有模板(可能導(dǎo)致其他測試失敗),并允許你基于新模板,增量逐層構(gòu)建出新的模板。

斷言模板具有以下 API:

insertBefore(selector: string, children: () => DNode[]): AssertionTemplateResult;
insertAfter(selector: string, children: () => DNode[]): AssertionTemplateResult;
insertSiblings(selector: string, children: () => DNode[], type?: 'before' | 'after'): AssertionTemplateResult;
append(selector: string, children: () => DNode[]): AssertionTemplateResult;
prepend(selector: string, children: () => DNode[]): AssertionTemplateResult;
replaceChildren(selector: string, children: () => DNode[]): AssertionTemplateResult;
setChildren(selector: string, children: () => DNode[], type?: 'prepend' | 'replace' | 'append'): AssertionTemplateResult;
setProperty(selector: string, property: string, value: any): AssertionTemplateResult;
setProperties(selector: string, value: any | PropertiesComparatorFunction): AssertionTemplateResult;
getChildren(selector: string): DNode[];
getProperty(selector: string, property: string): any;
getProperties(selector: string): any;
replace(selector: string, node: DNode): AssertionTemplateResult;
remove(selector: string): AssertionTemplateResult;

Mocking

您可能已經(jīng)注意到,在測試部件時,我們主要測試對屬性進行各種修改后,用戶界面是否正確渲染。它們不包含真正的業(yè)務(wù)邏輯,但您可能想測試例如單擊按鈕后是否調(diào)用了屬性方法。這個測試不關(guān)心方法實際做了什么,只關(guān)心是否正確調(diào)用了接口。在這種情況下,您可以使用類似 Sinon 庫。

src/widgets/Action.ts

import WidgetBase from '@dojo/framework/widget-core/WidgetBase';
import { v, w } from '@dojo/framework/widget-core/d';
import Button from '@dojo/widgets/button';

import * as css from './styles/Action.m.css';

export default class Action extends WidgetBase<{ fetchItems: () => void }> {
    protected render() {
        return v('div', { classes: [css.root] }, [w(Button, { onClick: this.handleClick, key: 'button' }, ['Fetch'])]);
    }
    private handleClick() {
        this.properties.fetchItems();
    }
}

您可能想測試,當(dāng)單擊按鈕后是否會調(diào)用 this.properties.fetchItems 方法。

tests/unit/widgets/Action.ts

const { describe, it } = intern.getInterface('bdd');
import harness from '@dojo/framework/testing/harness';
import { w, v } from '@dojo/framework/widget-core/d';

import { stub } from 'sinon';

describe('Action', () => {
    const fetchItems = stub();
    it('can fetch data on button click', () => {
        const h = harness(() => w(Home, { fetchItems }));
        h.expect(() => v('div', { classes: [css.root] }, [w(Button, { onClick: () => {}, key: 'button' }, ['Fetch'])]));
        h.trigger('@button', 'onClick');
        assert.isTrue(fetchItems.calledOnce);
    });
});

在這種情況下,你可以 mock 一個 Action 部件的 fetchItems 方法,該方法將嘗試獲取數(shù)據(jù)項。然后就可以使用 @button 定位到按鈕,并觸發(fā)按鈕的 onClick 事件,然后校驗 fetchItems 方法是否被調(diào)用過一次。

要了解更多 mocking 信息,請閱讀 Sinon 文檔。

功能測試

與單元測試加載和執(zhí)行代碼的流程不同,功能測試在瀏覽器中加載一個頁面并測試應(yīng)用程序的交互功能。

如果要校驗?zāi)硞€路由對應(yīng)的頁面內(nèi)容,可以通過更新鏈接來簡化測試。

src/widgets/Menu.ts

import WidgetBase from '@dojo/framework/widget-core/WidgetBase';
import { w } from '@dojo/framework/widget-core/d';
import Link from '@dojo/framework/routing/ActiveLink';
import Toolbar from '@dojo/widgets/toolbar';

import * as css from './styles/Menu.m.css';

export default class Menu extends WidgetBase {
    protected render() {
        return w(Toolbar, { heading: 'My Dojo App!', collapseWidth: 600 }, [
            w(
                Link,
                {
                    id: 'home', // add id attribute
                    to: 'home',
                    classes: [css.link],
                    activeClasses: [css.selected]
                },
                ['Home']
            ),
            w(
                Link,
                {
                    id: 'about', // add id attribute
                    to: 'about',
                    classes: [css.link],
                    activeClasses: [css.selected]
                },
                ['About']
            ),
            w(
                Link,
                {
                    id: 'profile', // add id attribute
                    to: 'profile',
                    classes: [css.link],
                    activeClasses: [css.selected]
                },
                ['Profile']
            )
        ]);
    }
}

在使用應(yīng)用程序時,您需要單擊 profile 鏈接,然后被導(dǎo)航到歡迎用戶頁面。你可以編寫一個功能測試來驗證此行為。

tests/functional/main.ts

const { describe, it } = intern.getInterface('bdd');
const { assert } = intern.getPlugin('chai');

describe('routing', () => {
    it('profile page correctly loads', ({ remote }) => {
        return (
            remote
                // loads the HTML file in local node server
                .get('../../output/dev/index.html')
                // find the id of the anchor tag
                .findById('profile')
                // click on the link
                .click()
                // end this action
                .end()
                // find the h2 tag
                .findByTagName('h2')
                // get the text in the h2 tag
                .getVisibleText()
                .then((text) => {
                    // verify the content of the h2 tag on the profile page
                    assert.equal(text, 'Welcome Dojo User!');
                })
        );
    });
});

當(dāng)運行功能測試時,Dojo 會提供一個與頁面交互的 remote 對象。因為加載頁面和與頁面交互是異步操作,所以必須在測試中返回 remote 對象。

可在命令行中執(zhí)行功能測試。

命令行

npm run test:functional

這會將 HTML 頁面加載到您計算機中 Chrome 的 remote 實例中,以測試交互功能。

功能測試是非常有用的,它能確保在瀏覽器中,您的程序代碼能按預(yù)期正常工作。

您可以閱讀更多關(guān)于 Intern 功能測試 的內(nèi)容。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI