原文连接:Testing React app with Cypresshtml
做者:Adam Trzciński前端
两周(原文发布于2017/11/6)之前,Cypress 开源了而且适用于任何人。react
Cypress 是一个工具,它使得你的端对端测试写起来更快。json
对浏览器中运行的任何内容进行快速,简单和可靠的测试。redux
让咱们来试一试,并验证这是真的!后端
咱们将把 Cypress 与咱们的项目之一--Eedi
集成在一块儿。 Eedi
是英国教师、学生及家长的绝佳教育平台。关键是,任何使用它的人,在浏览时都有愉快而流畅的体验,而且全部的功能都能按预期工做。浏览器
在咱们应用程序的根目录下,让咱们添加 Cypress 做为 dev 依赖。bash
$ yarn add --dev cypress
复制代码
调整 package.json
中的 "scripts":服务器
"scripts": {
...
"cypress:open": "cypress open"
}
复制代码
就像正常开发同样,在本地运行服务,而后在新的终端窗口中打开 Cypress:微信
$ yarn run cypress:open
复制代码
在这里,咱们能够访问咱们全部的测试,甚至开箱即用。
Cypress 已建立新的文件夹cypress
与子文件夹 fixtures
, integration
和support
。它还添加了一个空配置文件cypress.json
。
因为咱们常常访问咱们的根路径,所以将它抽象为配置文件是一种很好的作法。打开cypress.json
文件,并添加一个带有键baseUrl
和 url 的新条目做为值:
{
"baseUrl": "http://localhost:3000"
}
复制代码
在example_spec.js
文件中,咱们能够看到'Kitchen Sink Tests',当咱们想要浏览一些常见的测试场景时能够派上用场。可是让咱们如今写咱们本身的测试。
登陆是任何应用程序最重要的功能之一。若是作得很差,用户将没法看到咱们其它的工做,而且再作其余事情就没有任何意义。
建立一个新文件login_spec.js
。在这里,咱们将测试咱们关于登陆的全部逻辑。
让咱们写下咱们的第一个测试,让咱们来检查一下 happy path 是否如预期同样工做:
describe('Log In', () => {
it('succesfully performs login action', () => {
// 访问 'baseUrl'
cy.visit('/');
// 断言咱们是否处于好的位置 - 搜索'smarter world'
cy.contains('smarter world');
// 搜索带有 'Teachers' 的div, 并点击它
cy.get('a[data-testid="main-link-teachers"]').click();
// 检查url是否改变
cy.url().should('includes', 'teachers');
cy.contains('more time to teach');
// 找到Login按钮并点击它
cy.get('button[data-testid="menu-button-login"]').click();
// 检查url是否改变
cy.url().should('includes', '/login');
// 提交输入表单并点击提交按钮
cy.get('input[data-testid="login-form-username"]').type('test@email.com');
cy.get('input[data-testid="login-form-password"]').type('password');
cy.get('button[data-testid="login-form-submit"]').click();
// 验证是否被重定向
cy.url({ timeout: 3000 }).should('includes', '/c/');
});
});
复制代码
如今,请转到 Cypress 应用程序并选择咱们刚刚建立的测试。它应该在一个文件中运行全部的测试,咱们能够看到它们的表现如何:
让咱们停下来!修改测试的第12行:
cy.contains('Log In').click()
复制代码
添加更多的用例:
describe('Log In', () => {
it('succesfully performs login action', () => {
...
});
it('displays error message when login fails', () => {
// 直接转到登陆路径
cy.visit('/login');
// 尝试使用不正确的凭证登陆
cy.get('input[data-testid="login-form-username"]').type('test@email.com');
cy.get('input[data-testid="login-form-password"]').type('fail_password');
cy.get('button[data-testid="login-form-submit"]').click();
// 应该出现错误信息
cy.contains('Something went wrong');
});
it('redirects unauthorized users', () => {
// 转到受保护的路径
cy.visit('/c');
// 应该重定向到登陆页面
cy.url().should('contains', '/login');
});
});
复制代码
咱们保存测试文件以后,Cypress应该从新运行全部的测试:
下一个要覆盖的功能是注销操做。咱们但愿肯定该用户能够正确地从咱们的应用程序注销。听起来很简单,对吧?
可是,让咱们再考虑一下...为了注销,咱们须要先登陆,对吧?咱们是否应该重用先前测试的代码,而后再添加更多逻辑?听起来很傻,咱们是开发者,咱们能够作得更好!
Cypress 提供了另外一个便利的功能 -- 命令。它容许咱们建立能够在任何测试中重用的自定义操做。并且因为大多数场景应该为登陆用户编写,所以此操做是自定义命令的完美候选。
打开位于support
文件夹中的commands.js
文件。 Cypress 为咱们提供了一些示例,取消注释便可使用!
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
复制代码
使用咱们的自定义行为来加强此登陆命令,但首先让咱们考虑一下咱们想要作什么。
咱们已经测试了登陆,不是吗?因此,咱们接下来要写的每个测试都是重复相同的步骤,是没有意义的。咱们甚至能够阅读文档:
彻底测试登陆流程 - 但只有一次!
一样的:
在每次测试以前,请勿使用您的用户界面登陆。
那咱们能作什么呢?
咱们可使用cy.request()
直接向咱们的后端服务请求登陆,而后像往常同样继续。以下:
Cypress.Commands.add('login', (email, password) => {
// 向后端发出POST请求
// 咱们正在使用GraphQL,所以咱们正在经过转变:
cy
.request({
url: 'http://localhost:4000/graphql',
method: 'POST',
body: {
query:
'mutation login($email: String!, $password: String!) {loginUser(email: $email, password: $password)}',
variables: { email, password },
},
})
.then(resp => {
// 断言来自服务器的响应
expect(resp.status).to.eq(200);
expect(resp.body).to.have.property('data');
// 咱们全部的private路径都会检查存在redux store上的auth token,因此让咱们把它传递到那里
window.localStorage.setItem(
'reduxPersist:user',
JSON.stringify({ refreshToken: resp.body.data.loginUser })
);
// 到仪表盘
cy.visit('/c');
});
});
复制代码
如今,在每一个测试中,咱们能够调用cy.login('username','password')
,而且它应该执行登陆操做而不须要使用UI。
如今咱们准备测试注销操做,建立logout_spec.js
并添加一些断言:
const baseUrlMatcher = new RegExp('localhost:3000/$');
describe('Log out user properly', () => {
// 在每次测试前登陆:
beforeEach(() => {
cy.login('test@email.com', 'password');
});
it('can select dropdown and perform logout action', () => {
// 检查咱们是否登陆:
cy.url().should('contains', '/c/');
cy.get('div[data-testid="main-menu-settings"]').click();
cy
.get('.Popover-body ul li')
.first()
.click();
cy.url().should('match', baseUrlMatcher);
});
it('/logout url should work as well', () => {
cy.url().should('contains', '/c/');
cy.visit('/log-out');
cy.url().should('match', baseUrlMatcher);
});
it('should clear auth token from local storage', () => {
cy.url().should('contains', '/c/');
cy.visit('/logout');
cy.url().should('match', baseUrlMatcher);
const user = JSON.parse(window.localStorage.getItem('reduxPersist:user'));
assert.isUndefined(user.token, 'refreshToken is undefined');
});
});
复制代码
观察它们失败:
first()
更改成
last()
,将
cy.visit('log-out')
更改成
cy.visit('logout')
并观察测试如何经过:
总之,用 Cypress 写测试真的颇有趣。
正如所宣称的,配置几乎为零,编写断言很简单,感受很天然,并且 GUI 很是棒!您能够进行时间旅行,调试全部步骤,而且由于它们都做为 Electron 应用程序启动,因此咱们甚至能够访问开发者工具以了解每一个动做发生了什么。
网络已经进化,测试依旧会如此。
让咱们写一些测试吧,愿原力与你同在!
关注微信公众号:创宇前端(KnownsecFED),码上获取更多优质干货!