NodeJS实战--使用NodeJS实现前后端的联调

原文链接://www.greatytc.com/p/2a9367afe9e7

1510997059(1).jpg

1. 将原项目迁移进入KOA2,并顺利通过index/index路由进行访问

1.1 新建项目,配置项目结构所需的一系列的文件,使 index/index 可以打开

将原项目 Praisethumb 拖拽到 sublime 编辑器中,新建一个koatest 文件夹

打开小黑窗进入koatest 文件夹中

cd Desktop

cd koatest

初始化项目

npm init

找寻 koa2 包教程,www.npmjs.com-> 搜索框输入koa2

参考链接:https://www.npmjs.com/package/koa2

开始配置koa2 -> 在小黑窗中安装koa2包

npm install koa@next

在koatest文件夹中新建文件 app.js, copy 如下代码

const Koa = require('koa');

const app = new Koa();

// response

app.use(ctx => {

ctx.body = 'Hello Koa';

});

app.listen(3000);

由于使用的是 ES6 所以将 require 引用 改为 import name from 'name'

import Koa from 'koa';

const app = new Koa();

// response

app.use(ctx => {

ctx.body = 'Hello Koa';

});

app.listen(3000);

此时在小黑窗中输入 node app.js 会报如下的错误,原因是为将ES6 代码编译为 可解析的 ES5

4Y4V)NM0S7MLXBOBDM9NH2G.png

开始安装编译依赖包 babel ,在此阐述一下 -dev 的问题,安装包时加 -dev 说明其只在开发环境下使用而上线时有命令可以直接将其去掉, babel 便是此类的安装包

npm install babel-preset-es2015 --save-dev

http://www.ruanyifeng.com/blog/2016/01/babel.html?20170213113809阮一峰先生的 babel 教程

在项目中新建 .babelrc ,babel 的配置文件 输入如下代码

{

"presets":[

"es2015",

"stage-0"

],

"plugins":[]

}

安装babel 编译所需的其他包,stage-0 是 ES7 的一个阶段,是为了使 babel 可以识别后面写的 async 和 await

cnpm install babel-preset-stage-0 --save-dev

//www.greatytc.com/p/c84d52828e45async 和 await 详解

编译 app.js 文件,并启动服务

babel app.js -o app_o.js

node app_o.js

启动时报了如下错误,猜测是由于哪个包装的时候未装完整便退出了,所以将文件夹中的 node_modules 文件删除  再重新安装一遍项目所需的包 cnpm install  再次启动 node app_o.js 时便成功了

1511065654(1).jpg

启动成功后,在浏览器中输入http://localhost:3000/显示为 Hello Koa证明一切都没问题了

14.此时需要配置路由 为 /index/index ,使用 koa-simple-router 包

https://www.npmjs.com/package/koa-simple-router包的网址

npm install koa-simple-router --save

在 app.js 文件的代码改为如下所示的

import Koa from 'koa';

import router from 'koa-simple-router';

const app = new Koa();

app.use(router(_ => {

_.get('/', (ctx, next) => {

ctx.body = 'hello'

})

_.post('/name/:id', (ctx, next) => {

// ...

})

})

app.listen(3000);

为了使项目变得更加的分化明了

新建一个 midddleware 文件夹放项目的容错文件

新建一个 config 文件夹放项目的配置文件

新建一个 controller 文件夹放项目的路由配置文件

新建一个向后端请求数据的文件夹 models

新建一个放 html 页面的文件夹 views

再加一个放静态文件 .css .js 的文件夹 public

文件创建解构如下

public -> css -> index.css

public -> scripts -> index.js

controller -> initController.js

controller -> indexController.js

编辑 initController.js 文件,将 app.js 文件中的路由配置方法代码拿到 该文件中略加修改,最后讲方法导出

import index from './indexController';//引入 indexController 文件导出的方法

const controllerInit = {

//配置初始化函数

init(app, router) {

app.use(router(_ => {

_.get('/index/index', index.index())

}))

}

}

//导出初始化方法,使其全局可用

export default controllerInit;

编辑 indexController.js 文件,并导出该方法给 initController.js 文件使用

const indexController ={

index(){

return async(ctx,next)=>{

ctx.body = await ctx.render('index.html',function(){

title:'大拇指点赞'

})

}

}

}

// 导出该方法给 initController.js 文件使用

export default indexController;

在 app.js 文件中引用 initController 并执行

import initController from './controller/initController';

initController.init(app,router);

由于在controller js 文件中都使用了 koa-swig 框架中的 rander 函数,而且需要这个框架在静态文件中引用文件,所以要引用 koa-swig

https://www.npmjs.com/package/koa-swig包的网址

npm install koa-swig --save

https://www.cnblogs.com/elementstorm/p/3142644.htmlswig 使用指南

在 swig 使用指南中找到 模板继承模块,在项目中的 views 文件夹中新建 layout.html 和 index.html 将如下代码 copy 进去

layout.html

{% block title %}My Site{% endblock %}

{% block head %}

{% endblock %}

{% block content %}{% endblock %}

index.html

{% extends 'layout.html' %}

{% block title %}My Page{% endblock %}

{% block head %}

{% parent %}

{% endblock %}

{% block content %}

This is just an awesome page.

{% endblock %}

以为用的是 koa2 所以用 swig 时还要引用 co 模块,该模块无需安装,在 app.js 文件中输入如下代码

import co from 'co';

app.context.render = co.wrap(render({

root: __dirname + 'views',

autoescape: true,

cache: 'memory', // disable, set to false

ext: 'html',

writeBody: false

}));

html 文件中相互引用路径时需要安装 koa-static 来配置

npm install koa-static --save

在 app.js 文件中输入如下代码

import serve from 'koa-static';

app.use(serve(__dirname + '/test/fixtures'));

在 config 文件夹中新建  config.js 文件

http://es6.ruanyifeng.com/?search=map&x=0&y=0#docs/set-mapES

6-Map 方法详述

安装 path

npm  install path --save-dev

编辑 config.js 文件,将方法导出 app.js 会使用

import path from 'path';

const CONFIG = new Map();

CONFIG.set('port',3000);

CONFIG.set('staticDir',path.join(__dirname,'..','public'));

CONFIG.set('viewDir',path.join(__dirname,'..','views'));

export default CONFIG;

在 app.js 文件中引入 CONFIG 并修改配置路径

import CONFIG from './config/config';

initController.init(app,router);

app.context.render = co.wrap(render({

root: CONFIG.get('viewDir'),

autoescape: true,

cache: 'memory', // disable, set to false

ext: 'html',

writeBody: false

}));

app.use(serve(CONFIG.get('staticDir')));

app.listen(CONFIG.get('port'));

编译文件

babel app.js -o app_o.js  //期间莫名其妙报了错,然后检查了下需要引用的包,看看 package.json 文件中是否存在没有的安装一遍,之后还是报错,便将 node_modules 文件夹删掉 在将所有的包重装了一遍 npm install 可以了

进入 config 和 controller 文件夹中,将里面的 .js 文件后缀名改为 .es,再在命令行中进行编译

cd config

babel config.es -o config.js

cd ..

cd controller

babel initController.es -o initController.js

babel indexController.es -o indexController.js

启动服务

cd..

node app_o.js

此时报了如下的错误,

1511073380(1).jpg

原因是在 indexController 中用到了 async 和 await 但是并没有编译的很好,因为 babel 默认是不会编译较高级的函数的,所以需要装两个包使其支持

cnpm install babel-register --save-dev

cnpm install babel-polyfill --save-dev

装好之后在 app.js 文件中引入

import babel_co from 'babel-core/register';

import babel_po from 'babel-pocyfill';

再编译一遍文件

babel app.js -o app_o.js

cd controller

babel indexcontroller

babel indexController.es -o indexController.js

运行服务 报错为如下图

image.png

在 app.js 文件中引入 CONFIG

import CONFIG from './config/config';

编译修改过的文件

babel app.js -o app_o.js

cd config

babel config.es -o config.js

切记每次修改了 .es 都需要在重新编译一遍

运行服务

node app_o.js

在浏览器打开页面http://localhost:3000/index/index展示如下图所示  便表示成功了接下来就是第二步将原项目迁移过来

1511076432(1).jpg

1.2 将之前所做的 项目 Praisethumb 中的文件配置到新项目中

1.将之前的 css 样式 放到 koatest 项目中的 index.css 文件中

{

margin: 0;

padding: 0;

}

body {

background-color: #b1c8ac;

padding: 50px;

position: relative;

height: 600px;

}

.a {

position: absolute;

left: 0;

right: 0;

top: 0;

bottom: 0;

margin: auto;

background-color: #ffcaaa;

width: 226px;

height: 360px;

border-radius: 38px 0 0 38px;

border: 1px solid #95755e;

border-width: 1px 0 1px 1px;

}

.a:before {

content: " ";

display: block;

position: absolute;

width: 246px;

height: 240px;

border-top: 1px solid #95755e;

bottom: 0;

top: 0;

right: 0;

left: 0;

margin: auto auto auto -224px;

background-color: #ffcaaa;

}

.a:after {

content: " ";

display: block;

position: absolute;

width: 150px;

height: 90px;

border: 1px solid #95755e;

border-width: 1px 1px 0 0;

border-radius: 0 45px 45px 0;

bottom: 0;

top: 0;

right: 0;

left: 0;

margin: 0 auto auto 30px;

background-color: #ffcaaa;

transform: rotate(-65deg);

transform-origin: left top;

box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset, -80px 2px 10px 1px #ffcaaa inset, -100px 2px 20px 0px #efb38f inset;

transition: all .3s;

}

.b {

width: 180px;

height: 90px;

border: 1px solid #95755e;

position: absolute;

border-radius: 45px;

box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;

background-color: #ffcaaa;

z-index: -1;

left: 0;

right: 0;

top: 0;

bottom: 0;

margin: auto -90px -1px auto;

-webkit-box-reflect: above 178px;

box-reflect: above 178px;

}

.b:before {

content: " ";

display: block;

position: absolute;

width: 180px;

height: 90px;

border: 1px solid #95755e;

right: 0;

top: -91px;

border-radius: 45px;

border-width: 1px 1px 1px 0;

box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;

background-color: #ffcaaa;

}

.c {

width: 180px;

height: 90px;

border: 1px solid #95755e;

position: absolute;

border-radius: 45px;

box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;

background-color: #ffcaaa;

z-index: -1;

left: 0;

right: 0;

top: 0;

bottom: 0;

margin: 90px -90px -1px auto;

/-webkit-box-reflect: above 178px;

box-reflect: above 178px;

/

}

.c:before {

content: " ";

display: block;

position: absolute;

width: 180px;

height: 90px;

border: 1px solid #95755e;

right: 0;

top: -91px;

border-radius: 45px;

border-width: 1px 1px 1px 0;

box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;

background-color: #ffcaaa;

}

/.b:after{content: " ";display: block;position: absolute;width: 180px;height:90px;border: 1px solid #95755e;right: 0;top:182px;border-radius: 45px;border-width: 1px 1px 1px 0;box-shadow:-4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;background-color: #ffcaaa;}/

.hide {

opacity: 0;

color: red;

font-size: 60px;

position: absolute;

right: 60px;

top: 150px;

}

.num {

animation: myani .6s ease;

-moz-animation: myani .6s ease;

-webkit-animation: myani .6s ease;

-o-animation: myani .6s ease;

}

@keyframes myani() {

form {

opacity: 0;

top: 150px;

}

to {

opacity: 1;

top: 50px;

}

}

@-moz-keyframes myani {

form {

opacity: 0;

top: 150px;

}

to {

opacity: 1;

top: 50px;

}

}

@-webkit-keyframes myani {

form {

opacity: 0;

top: 150px;

}

to {

opacity: 1;

top: 50px;

}

}

@-o-keyframes myani {

form {

opacity: 0;

top: 150px;

}

to {

opacity: 1;

top: 50px;

}

}

:root {

--green: #b1c8ac;

}

body {

background-color: var(--green);

}

将 html 内容也拿过来

index.html

{% extends 'layout.html' %}

{% block title %}My Page{% endblock %}

{% block head %}

{% parent %}

{% endblock %}

{% block content %}

+1

SystemJS.config({

//使用的 js 文件的文件夹路径

baseURL: '/scripts'

});

//使用的 js 文件名称

SystemJS.import('index-es.js').then(function(m) {

$.extend({

//将自己写的函数挂载到 jq 上称为一个插件

thumb:m.default.Thumb

})

//回调函数

callBack();

});

//定义回调函数

function callBack() {

//为页面元素添加方法

var f = new $.thumb(0,$('#thumb'));

f.clickAction();

}

{% endblock %}

拿 js

window.add = function (num) {

return num + 1;

}

将编译过的 点击小手子类继承父类的 js文件(index.es.js)复制到 项目的 scripts 文件夹下

index-es.js

'use strict';

Object.defineProperty(exports, "__esModule", {

value: true

});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.proto= superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var PraiseButton = function () {

function PraiseButton(num, element) {

_classCallCheck(this, PraiseButton);

this.num = num;

this.element = element;

_createClass(PraiseButton, [{

key: 'clickAction',

value: function clickAction() {

var _this = this;

this.element.click(function () {

if (_this.num < 10) {

_this.element.css('-webkit-filter', 'grayscale(0)');

$('#animation').addClass('num');

_this.num = add(_this.num);

setTimeout(function () {

$('#animation').removeClass('num');

}, 1000);

} else {

_this.element.css('-webkit-filter', 'grayscale(1)');

_this.num = 0;

}

console.log(_this.num);

});

}

}]);

return PraiseButton;

}();

var Thumb = function (_PraiseButton) {

_inherits(Thumb, _PraiseButton);

function Thumb(num, element) {

_classCallCheck(this, Thumb);

return _possibleConstructorReturn(this, (Thumb.proto|| Object.getPrototypeOf(Thumb)).call(this, num, element));

}

return Thumb;

}(PraiseButton);

exports.default = { Thumb: Thumb

// let  f = new Thumb(0,$('#thumb'));

// f.clickAction();

};

重启服务

node app_o.js

刷新http://localhost:3000/index/index页面,发现项目已经移植成功

2. 使用 PHP + MySQL 完成点赞借口,实现用户点击一次更新数据库点赞次数

在项目中新建一个 praise.php 文件

编辑文件


//用php的面向对象来写

/**

* 新建一个类

*/

class Conmysql

{

//定义所需的变量

//public关键字表示属性或方法是公开可见的

public $servername;

public $username;

public $password;

public $dbname;

}

?>

用 XAMPP 启动 Apache 和 MySQL

1511081397(1).jpg

在浏览器中打开http://localhost/phpmyadmin/页面

在左侧操作栏中点击【新建】

输入新数据库的名字 praise 点击【创建】

新建数据表 text 2

字段分别为  id 和 num

id 需要设置为主键

点击保存

具体设计如下图所示

image.png

在 text 数据表中手动添加一条数据

点击数据表【text】

点击 【SQL】

点击【INSERT】

将输入域的值修改为如下所示,点击【执行】

INSERT INTO `text`(`id`, `num`) VALUES (1,0)

在 php 文件中编辑


//用php的面向对象来写

/**

* 新建一个类

*/

class Conmysql

{

//定义所需的变量

//public关键字表示属性或方法是公开可见的

public $servername;

public $username;

public $password;

public $dbname;

public $con = null;

//添加构造方法

public function __construct($servername,$username,$password,$dbname){

$this->servername = $servername;

$this->username = $username;

$this->password = $password;

$this->dbname = $dbname;

}

//创建数据库链接方法

//参考网址:http://www.runoob.com/php/php-mysql-connect.html -> 实例 (PDO)

public function getConnection(){

//这里用的是 PDO 方法

try {

$dsn = "mysql:host=$this->servername;dbname=$this->dbname";

$this->con = new PDO($dsn, $this->username, $this->password);

//echo "连接成功";

}

catch(PDOException $e)

{

echo $e->getMessage();

}

}

//更新数据的方法

public function updateDate($sql){

//如果未连接的话就链接到数据库

if($this->con == null){

$this->getConnection();

}

//执行sql

//参考链接:http://www.runoob.com/php/php-mysql-insert.html -> 实例 (PDO)

$res=$this->con->exec($sql);

//关闭链接

$this->closeCon();

}

//关闭链接的方法

public function closeCon(){

$this->con = null;

}

}

/**

* 创建子类

*/

class realConn extends Conmysql

{

//继承父类的构造方法

public function __construct($servername,$username,$password,$dbname){

parent::__construct($servername,$username,$password,$dbname);

}

//真正执行 sql 的方法

public function updateRealDate(){

$sql = "UPDATE text SET num=num+1 WHERE id=1";

$this->updateDate($sql);

}

}

//将子类实例化执行方法

$praiseCon = new realConn('localhost','root','','praise');

$praiseCon->updateRealDate();

?>

将 praise.php 文件复制到 C:\xampp\htdocs 文件夹下,在浏览器中打开http://localhost/praise.php,再查看 text 数据表中会发现 num 的值为 1 ,刷 php 页面一次值便会加 1,至此点赞接口便完成了

3. 使用KOA2+ES6封装PHP点赞接口,并建立路由

1.在 koatest 项目文件夹的 models 文件夹中新建一个 indexmodel.js 文件

编辑 indexmodel.js 文件

import rpA from 'request-promise';

class indexModel {

constructor(ctx) {

this.ctx = ctx;

}

//为了实现数据库更新建立方法 传数据的接口

updateNum() {

//在这里 return 一个 promise 模块传入 initController.js 再在indexController.js 中调用需要用到一个模块 request-promise  参考网址:https://www.npmjs.com/package/request-promise

// 获取文件

const options = {

uri: 'http://localhost/praise.php',

method:'GET'

};

return new Promise((resolve,reject)=>{

rpA(options).then(function(result){

const info = JSON.parse(result);

if(info){

resolve({data:info.result});

}else{

reject({});

}

})

})

}

}

//将该方法导出,  indexController.js 文件要用到

export default indexModel;

由于 updateNum 方法中需要用到 request-promise 模块 所以需要安装两个包

参考链接:https://www.npmjs.com/package/request-promise

npm install --save request

npm install --save request-promise

编辑 indexController.js 文件

indexController 对象中增加一个 提交数据的方法

//引入所需方法的文件

import indexModel from '../models/indexModel';

update(){

return async(ctx,next)=>{

const indexM = new indexModel(ctx);

ctx.body = await indexM.updateNum();

}

}

编辑 initController.js 文件

将 indexController 提交数据的方法注册到 一个新的路由上

_.get('/index/update', index.update());

babel 编译文件

将 indexmodel.js 重命名为 indexmodel.es

cd models

babel indexmodel.es -o indexmodel.js

cd ..

cd controller

babel  indexController.es -o indexController.js

babel initController.es -o initController.js

此时运行服务会发现小手点击并没有与服务器关联起来,此时需要用 axios 模块来使其连接到 koa2 点赞接口上

4. 将用户点击事件通过 axios 链接到 KOA2 点赞接口

在 koatest 项目中的 scripts 文件夹中新建 index.es 文件,并将 praisethumb 项目中的 scripts -> index.js 文件代码复制过来

class PraiseButton{

constructor(num,element){

this.num = num;

this.element = element;

}

clickAction(){

this.element.click(()=>{

if(this.num < 10){

this.element.css('-webkit-filter','grayscale(0)');

$('#animation').addClass('num');

this.num = add(this.num);

setTimeout(function () {

$('#animation').removeClass('num');

},1000);

}else{

this.element.css('-webkit-filter','grayscale(1)');

this.num = 0;

}

console.log(this.num);

})

}

}

class Thumb extends PraiseButton{

constructor(num,element){

super(num,element)

}

}

export default{Thumb}

// let  f = new Thumb(0,$('#thumb'));

// f.clickAction();

编辑 index.es 文件,这里是要用 axios 模块来使其连接到 koa2 点赞接口上

参考链接:https://www.npmjs.com/package/axios

这里有三种引用的方法:npm 装包,浏览器装包,cdn,这里我们用的是 cdn 方法,将如下代码 copy 至 index.html 文件中

将如下代码 copy 至 index.es -> this.element.click 点击事件中

axios.get('/index/update')

.then(function (response) {

console.log(response);

})

.catch(function (error) {

console.log(error);

});

编修改过的文件 index.es

cd public\scripts\

babel index.es -o index-es.js

运行服务

cd ../..

node app_o.js

点击时页面http://localhost:3000/index/index报如下图所示的错误

image.png

编辑 praise.php 文件中的 updateDate 方法

//更新数据的方法

public function updateDate($sql){

//如果未连接的话就链接到数据库

if($this->con == null){

$this->getConnection();

}

//向前台输出 json 格式的数据

header('content-type:application/json;charset=utf8');

//执行sql

//参考链接:http://www.runoob.com/php/php-mysql-insert.html -> 实例 (PDO)

$res=$this->con->exec($sql);

//执行之后 输出返回的数据

$arr = array('result'=>$res);

echo json_encode($arr);

//关闭链接

$this->closeCon();

}

运行服务,发现又报了 404 ,排查之后发现是 index.es 文件中的 uodate 写成了 uodete ,诶,修改后 编译 ,再运行点击一下小手,数据库里面的 num 的值 +1 证明成功了

需要注意的是 php返回的数据必须严格是 json 格式的 不然前台 .js 文件 (未编译:indexmodel.es 已编译 indexmodel.js)中用到的 JSON.parse(result); 会一直报错导致页面崩溃

cd public\scripts\

babel index.es -o index-es.js

cd../..

node app_o.js

5. 对用户连续点击事件进行稀释

稀释所用的方法就是 setTimeout

编辑 index.es 文件

let f = '';

class PraiseButton{

constructor(num,element){

this.num = num;

this.element = element;

}

clickAction(){

this.element.click(()=>{

//判断事件是否已经存在,如果存在便会将其清除掉,所以点击只会执行最后一次点击的那次事件

if(f){

clearTimeout(f);

}

f = setTimeout(()=>{

if(this.num < 10){

this.element.css('-webkit-filter','grayscale(0)');

$('#animation').addClass('num');

this.num = add(this.num);

setTimeout(function () {

$('#animation').removeClass('num');

},1000);

axios.get('/index/update')

.then(function (response) {

console.log(response);

})

.catch(function (error) {

console.log(error);

});

}else{

this.element.css('-webkit-filter','grayscale(1)');

this.num = 0;

}

console.log(this.num);

},800)

})

}

}

class Thumb extends PraiseButton{

constructor(num,element){

super(num,element)

}

}

export default{Thumb}

// let  f = new Thumb(0,$('#thumb'));

// f.clickAction();

编译文件

cd public\scripts\

babel index.es -o index-es.js

运行服务,稀释成功

node app_o.js

6. 完成点赞接口的自动化测试、点赞+1功能的自动化测试、真实页面点击自动化测试

1. 点赞+1功能的自动化测试

安装 karma

cnpm install karma --save-dev

安装断言库、核心、chrome 启动器

cnpm install karma-jasmine jasmine-core karma-chrome-launcher --save-dev

安装无界面浏览器、无界面浏览器启动器

cnpm install phantomjs --save-dev

cnpm install karma-phantomjs-launcher --save-dev

karma 初始化生成配置文件 karma.conf.js

karma init

jasmine

no

PhantomJS

yes

5.在项目中新建一个 test 文件夹,在该文件夹下做三大测试

将 praisethumb 项目文件夹的中 index.spce.js 文件复制到 test 文件夹下

编辑 karma.conf.js 文件

files: [

'test/index.spec.js',

'public/scripts/index.js'

],

singleRun: true,

启动测试,运行成功

karma start

2. 点赞接口的自动化测试

1.先安装 mocha

cnpm install mocha --save-dev

安装 supertest

cnpm install supertest --save-dev

在 test 文件夹下新建一个 server.js 文件

编辑 sever.js 文件,这里我们需要用到 supertest 模块

参考链接:https://www.npmjs.com/package/supertest

import requestsuper from 'supertest';

//这里引用的是 app_o.js 文件拿到 app,所以需要在 app.js 文件中将其导出,然后再将 app.js 文件编译为 app_o.js 文件

import app from '../app_o.js';

//先定义一个拿到端口的方法

function request(){

return requestsuper(app.listen());

}

describe('测试路由', function() {

it('点赞', function(done) {

request()

.get('/index/update')

.expect(200)

.end(function(err,res){

if(res.data==1)return done(err);

done();

})

});

});

修改 app.js 文件

export default app;

编译文件

babel app.js -o app_o.js

cd test\

babel server.js -o server-es.js

执行 server-es.js 文件

cd ..

mocha test\server-es.js

此时报如下图所示的错误

image.png

将 mocha 安装至全局

cnpm install mocha -g

执行测试

mocha test\server-es.js

执行成功

image.png

3. 真实页面点击自动化测试

1.安装 selenium-webdriver

参考链接:https://www.npmjs.com/package/selenium-webdriver

cnpm install selenium-webdriver

2.下面火狐浏览器所需的相应组件

参考链接:https://github.com/mozilla/geckodriver/releases/找到与当前设备匹配的一款点击下载 我的是 win64

将下载好的压缩包解压至 koatest 项目文件夹中,删除压缩包

再安装两个包

cnpm install selenium-standalone --save

cnpm install protractor --save

test 文件夹下新建 e2e.js 文件

编辑 e2e.js 文件

参考链接:https://www.npmjs.com/package/selenium-webdriver->  Usage

const {Builder, By, Key, until} = require('selenium-webdriver');

let driver = new Builder()

.forBrowser('firefox')

.build();

//设置地址

driver.get('http://localhost:3000/index/index');

driver.findElement(By.id('thumb')).click();

const _animation = driver.findElement(By.id('animation'))

driver.wait(_animation.isDisplayed(), 1000);

// driver.quit(); //为了看到效果将关闭浏览器的行为暂时注释掉

执行测试脚本 e2e.js 文件

node test\e2e.js

此时会发现浏览器打开页面后,页面并不能加载出来原因是这个 koatest 项目此时并没有处于运行状态,所以需要新建一个命令行窗口中执行项目运行操作

image.png

在新窗口中输入命令

cd Desktop

cd koatest

node app_o.js

此时再在之前的命令行窗口中执行测试脚本 e2e.js 文件

node test\e2e.js

整个实战到此结束。

作者:sunxiaochuan

链接://www.greatytc.com/p/2a9367afe9e7

來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容