Koa1 框架

Koa是八个类似于Express的Web开发框架,创办者也是同一人。它的最重要特点是,使用了ES6的Generator函数,实行了架构的再度设计。也正是说,Koa的规律和内部结构很像Express,然则语法和内部结构举行了晋级。

 
本来的打算是翻新完8之后呢,中期就不在更新了,近日学习flask后,手又想付出个阳台吗,吧作者后边写的框架给选取上,形成1个简练的接口测试化平台吗,–IAPTest 

官方faq有那般多个难题:”为啥koa不是Express
4.0?“,回答是那般的:”Koa与Express有非常大差别,整个布置都是例外的,所以如若将Express
3.0安份守己那种写法升级到4.0,就代表重写整个程序。所以,大家以为创立3个新的库,是更妥帖的做法。“

本人给她起名,有没有感觉很霸气啊,其实撤这么多不释迦牟尼点使用的,其实做那一个平台吗,愈来愈多的是对flask学习的3个行使呢。当时协调思想了一下午,其实也不知底大家想要的都以什么样样子的,所以呢,就早先做了上边包车型客车筹划。

Koa应用

二个Koa应用便是二个指标,包括了三个middleware数组,那么些数组由一组Generator函数组成。这一个函数负责对HTTP请求实行各类加工,比如生成缓存、钦赐代理、请求重定向等等。

1 var koa = require('koa');
2 var app = koa();
3 
4 app.use(function *(){
5   this.body = 'Hello World';
6 });
7 
8 app.listen(3000);

要安装koa才能测试

地方代码中,变量app正是3个Koa应用。它监听3000端口,重临三个剧情为Hello
World的网页。

app.use方法用于向middleware数组添加Generator函数。

listen方法内定监听端口,并运行当前应用。它实际等同上边包车型地铁代码

1 var http = require('http');
2 var koa = require('koa');
3 var app = koa();
4 http.createServer(app.callback()).listen(3000);

图片 1

中间件

上边是3个两个中间件级联的事例

1 app.use(function *() {
2   this.body = "header\n";
3   yield saveResults.call(this);
4   this.body += "footer\n";
5 });
6 
7 function *saveResults() {
8   this.body += "Results Saved!\n";
9 }

上边代码中,第5当中间件调用第四在那之中间件saveResults,它们都向this.body写入内容。最后,this.body的出口如下。

 1 header 2 Results Saved! 3 footer 

若是有贰在那之中间件贫乏yield next说话,前面包车型大巴中间件都不会进行,那一点要引起注意。

若是想跳过2个中间件,能够间接在该中间件的第二行语句写上return yield next

1 app.use(function* (next) {
2   if (skip) return yield next;
3 })

大题上,第1版的demo版本的急需正是这般的,因为此处没有考虑任何太多,
那么接下去正是来初阶选拔了,笔者选取的是flask 框架,前端呢
,小编用的是bootstarp框架,

路由

能够通过this.path属性,判断用户请求的门径,从而起到路由功能。

 1 app.use(function* (next) {
 2   if (this.path === '/') {
 3     this.body = 'we are at home!';
 4   } else {
 5     yield next;
 6   }
 7 })
 8 
 9 // 等同于
10 
11 app.use(function* (next) {
12   if (this.path !== '/') return yield next;
13   this.body = 'we are at home!';
14 })

上面是多路径的例证。

 1 let koa = require('koa')
 2 
 3 let app = koa()
 4 
 5 // normal route
 6 app.use(function* (next) {
 7   if (this.path !== '/') {
 8     return yield next
 9   }
10 
11   this.body = 'hello world'
12 });
13 
14 // /404 route
15 app.use(function* (next) {
16   if (this.path !== '/404') {
17     return yield next;
18   }
19 
20   this.body = 'page not found'
21 });
22 
23 // /500 route
24 app.use(function* (next) {
25   if (this.path !== '/500') {
26     return yield next;
27   }
28 
29   this.body = 'internal server error'
30 });
31 
32 app.listen(8080)

地点代码中,每1个中间件负责贰个门路,假若路径不切合,就传递给下壹当中间件。

复杂的路由需求设置koa-router插件。

 1 var app = require('koa')();
 2 var Router = require('koa-router');
 3 
 4 var myRouter = new Router();
 5 
 6 myRouter.get('/', function *(next) {
 7   this.response.body = 'Hello World!';
 8 });
 9 
10 app.use(myRouter.routes());
11 
12 app.listen(3000);

地方代码对根路径设置路由。

Koa-router实例提供一多元动词方法,即一种HTTP动词对应一种方法。典型的动词方法有以下八种。

  • router.get()
  • router.post()
  • router.put()
  • router.del()
  • router.patch()

那几个动词方法还不错多少个参数,第3个是途径方式,第一个是对应的控制器方法(中间件),定义用户请求该路线时服务器行为。

1 router.get('/', function *(next) {
2   this.body = 'Hello World!';
3 });

上边代码中,router.get艺术的第三个参数是根路径,第②个参数是呼应的函数方法。

只顾,路径匹配的时候,不会把询问字符串考虑在内。比如,/index?param=xyz匹配路径/index

有个别路径情势相比较复杂,Koa-router允许为路径情势起外号。起名时,外号要加上为动词方法的首先个参数,那时动词方法成为接受四个参数。

1 router.get('user', '/users/:id', function *(next) {
2  // ...
3 });

上面代码中,路径模式\users\:id的名字就是user。路径的名称,可以用来引用对应的具体路径,比如url方法可以根据路径名称,
结合给定的参数,生成具体的路径。

Koa-router允许为路径统一添加前缀。

1 var router = new Router({
2   prefix: '/users'
3 });
4 
5 router.get('/', ...); // 等同于"/users"
6 router.get('/:id', ...); // 等同于"/users/:id"

途径的参数通过this.params属性获取,该属性重临3个对象,全体途径参数都以该目的的成员。

// 访问 /programming/how-to-node
router.get('/:category/:title', function *(next) {
  console.log(this.params);
  // => { category: 'programming', title: 'how-to-node' }
});

param方法能够针对命名参数,设置验证条件。

 1 router
 2   .get('/users/:user', function *(next) {
 3     this.body = this.user;
 4   })
 5   .param('user', function *(id, next) {
 6     var users = [ '0号用户', '1号用户', '2号用户'];
 7     this.user = users[id];
 8     if (!this.user) return this.status = 404;
 9     yield next;
10   })

上边代码中,要是/users/:user的参数user对应的不是立竿见影用户(比如访问/users/3),param方法注册的中间件会查到,就会回到404错误。

redirect方法会将某些路径的请求,重定向到另三个门道,并赶回301状态码。

1 router.redirect('/login', 'sign-in');
2 
3 // 等同于
4 router.all('/login', function *() {
5   this.redirect('/sign-in');
6   this.status = 301;
7 });

redirect方法的第3个参数是伸手来源,第二个参数是指标地,两者都可以用路径形式的小名代替。

那就是说接下去,小编是要对自笔者的数据库举办规划。那里要提的是自己对权力的安顿性吧,笔者越来越多的是挑选了一个较为不难的法门,我去用三个字段去开始展览权力控制,小编利用level字段,
0就代表自个儿这几个用户是普通用户,1正是管理员用户,那里很多地点的删除都做了协会者和非管理员的差异,管理员能够对具备的展开删除,可是平日管理是无法的,
用户管理界面只可以管理员去运作这么些。

错误处理机制

1 app.use(function *() {
2   try {
3     yield saveResults();
4   } catch (err) {
5     this.throw(400, '数据无效');
6   }
7 });

地方代码自行布置了try…catch代码块,一旦发生错误,就用this.throw格局抛出。该办法能够将钦赐的状态码和错误新闻,重返给客户端。

对此未捕获错误,能够安装error事件的监听函数。

1 app.on('error', function(err){
2   log.error('server error', err);
3 });

this.throw方法用于向客户端抛出1个不当。

 1 this.throw(403);
 2 this.throw('name required', 400);
 3 this.throw(400, 'name required');
 4 this.throw('something exploded');
 5 
 6 this.throw('name required', 400)
 7 // 等同于
 8 var err = new Error('name required');
 9 err.status = 400;
10 throw err;

this.throw艺术的多少个参数,2个是错误码,另2个是报错新闻。假设省略状态码,暗中同意是500错误。

this.assert措施用于在中间件之中断言,用法类似于Node的assert模块

1 this.assert(this.user, 401, 'User not found. Please login!');

地点代码中,要是this.user属性不存在,会抛出一个401荒唐。

上面给下小编数据库方面包车型大巴代码片段。

cookie

cookie的读取和设置。

1 this.cookies.get('view');
2 this.cookies.set('view', n);

get和set方法都还行第多少个参数,表示配置参数。个中的signed参数,用于内定cookie是不是加密。借使钦定加密的话,必须用app.keys钦定加密短语。

1 app.keys = ['secret1', 'secret2'];
2 this.cookies.set('name', '张三', { signed: true });

this.cookie的布局对象的习性如下。

  • signed:cookie是还是不是加密。
  • expires:cookie哪一天过期
  • path:cookie的路径,默认是“/”。
  • domain:cookie的域名。
  • secure:cookie是还是不是只有https请求下才发送。
  • httpOnly:是不是唯有服务器能够取到cookie,暗中同意为true。
class User(db.Model):
    __tablename__='users'
    id=db.Column(db.Integer(),primary_key=True,autoincrement=True)
    username=db.Column(db.String(63),unique=True)
    password=db.Column(db.String(252))
    user_email=db.Column(db.String(64),unique=True)
    status=db.Column(db.Integer(),default=0)
    level=db.Column(db.Integer(),default=0)
    phone = db.relationship('TestResult', backref='users', lazy='dynamic')
    def __repr__(self):
        return  self.username
    def set_password(self,password):
        self.password=generate_password_hash(password)
    def check_password(self,password):
        return  check_password_hash(self.password,password)
class Interface(db.Model):
    __tablename__='interfaces'
    id=db.Column(db.Integer(),primary_key=True,autoincrement=True)
    project_name=db.Column(db.String(252))
    models_name=db.Column(db.String(252))
    Interface_name=db.Column(db.String(252))
    Interface_url=db.Column(db.String(252))
    Interface_meth= db.Column(db.String(252),default='GET')
    Interface_par=db.Column(db.String(252))
    Interface_back=db.Column(db.String(252))
    Interface_user_id=db.Column(db.Integer(),db.ForeignKey('users.id'))
    def __repr__(self):
        return  self.Interface_name
class InterfaceTest(db.Model):
    __tablename__='interfacetests'
    id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
    project=db.Column(db.String(252))
    model=db.Column(db.String(252))
    Interface_name= db.Column(db.String(252))
    Interface_url = db.Column(db.String(252))
    Interface_meth = db.Column(db.String(252))
    Interface_pase = db.Column(db.String(252))
    Interface_assert=db.Column(db.String(252))
    Interface_user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
    def __repr__(self):
        return  self.yongli_name
class TestResult(db.Model):
    __tablename__='tstresults'
    id=db.Column(db.Integer(),primary_key=True,autoincrement=True)
    Test_user_id=db.Column(db.Integer(),db.ForeignKey('users.id'))
    test_num=db.Column(db.Integer())
    pass_num=db.Column(db.Integer())
    fail_num = db.Column(db.Integer())
    skip_num=db.Column(db.Integer())
    test_time=db.Column(db.DateTime(),default=datetime.datetime.now())
    hour_time=db.Column(db.Integer())
    test_rep=db.Column(db.String(252))
    test_log=db.Column(db.String(252))
    def __repr__(self):
        return  self.id

session

 1 var session = require('koa-session');
 2 var koa = require('koa');
 3 var app = koa();
 4 
 5 app.keys = ['some secret hurr'];
 6 app.use(session(app));
 7 
 8 app.use(function *(){
 9   var n = this.session.views || 0;
10   this.session.views = ++n;
11   this.body = n + ' views';
12 })
13 
14 app.listen(3000);
15 console.log('listening on port 3000');

 1 可以把session存到mysql中
 2 安装npm install koa-generic-session --save-dev
 3 2.app.js中
 4 var session = require('koa-generic-session');
 5 
 6 app.keys = ['my secret key'];  // needed for cookie-signing,设置一个签名 Cookie 的密钥
 7 app.use(session());
 8 
 9 3.
10 this.session.loginbean
11 
12 方法二:
13 session映射到mysql
14 1.加安装
15 npm install mysql --save-dev
16 npm install koa-mysql-session --save-dev
17 
18 app.js中:
19 var session = require('koa-generic-session');
20 const mysql = require('mysql');
21 const MysqlStore = require('koa-mysql-session');
22 
23 app.keys = ['my secret key'];  // needed for cookie-signing,设置一个签名 Cookie 的密钥
24 app.use(session({store:new MysqlStore({
25   host: 'localhost',       //主机  
26   user: 'root',               //MySQL认证用户名  
27   password: 'root',        //MySQL认证用户密码  
28   database: 'kameng',  
29   port: '3306',                   //端口号 
30   acquireTimeout:0
31 })}));

  那里的代码呢,作者就不一一赘述,
接口,接口测试用例,接口执行结果,都以和用户表一对多的涉及,有了数据表,大家就能够去创设大家的数据库了,在开立的时候,大家还亟需加2个安插文件,那里存放着部分数据库的焦点配备相关的。

Request对象

Request对象表示HTTP请求。

(1)this.request.header

回去1个对象,包涵全体HTTP请求的头音信。它也足以写成this.request.headers

(2)this.request.method

回来HTTP请求的措施,该属性可读写。

(3)this.request.length

重临HTTP请求的Content-Length属性,取不到值,则重临undefined。

(4)this.request.path

回去HTTP请求的门道,该属性可读写。

(5)this.request.href

归来HTTP请求的完全路径,包涵协议、端口和url。

1 this.request.href
2 // http://example.com/foo/bar?q=1

(6)this.request.querystring

回到HTTP请求的查询字符串,不含问号。该属性可读写。

(7)this.request.search

再次来到HTTP请求的查询字符串,含问号。该属性可读写。

(8)this.request.host

重回HTTP请求的主机(含端口号)。

(9)this.request.hostname

重返HTTP的主机名(不含端口号)。

(10)this.request.type

返回HTTP请求的Content-Type属性

1 var ct = this.request.type;
2 // "image/png"

(11)this.request.charset

回到HTTP请求的字符集。

1 this.request.charset
2 // "utf-8
SECRET_KEY = 'BaSeQuie'
    basedir=os.path.abspath(os.path.dirname(__file__))
    SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "data.sqlite")
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    SQLALCHEMY_TRACK_MODIFICATIONS=False
    CSRF_ENABLED = True

路由

能够透过this.path质量,判断用户请求的门径,从而起到路由成效。

app.use(function* (next) {
  if (this.path === '/') {
    this.body = 'we are at home!';
  } else {
    yield next;
  }
})

// 等同于

app.use(function* (next) {
  if (this.path !== '/') return yield next;
  this.body = 'we are at home!';
})

上边是多路径的例子。

let koa = require('koa')

let app = koa()

// normal route
app.use(function* (next) {
  if (this.path !== '/') {
    return yield next
  }

  this.body = 'hello world'
});

// /404 route
app.use(function* (next) {
  if (this.path !== '/404') {
    return yield next;
  }

  this.body = 'page not found'
});

// /500 route
app.use(function* (next) {
  if (this.path !== '/500') {
    return yield next;
  }

  this.body = 'internal server error'
});

app.listen(8080)

地点代码中,每壹当中间件负责2个门路,假如路径不适合,就传递给下1在那之中间件。

复杂的路由供给安装koa-router插件。

var app = require('koa')();
var Router = require('koa-router');

var myRouter = new Router();

myRouter.get('/', function *(next) {
  this.response.body = 'Hello World!';
});

app.use(myRouter.routes());

app.listen(3000);

上边代码对根路径设置路由。

Koa-router实例提供一文山会海动词方法,即一种HTTP动词对应一种办法。典型的动词方法有以下多样。

  • router.get()
  • router.post()
  • router.put()
  • router.del()
  • router.patch()

这么些动词方法还不错八个参数,第③个是路径情势,首个是呼应的控制器方法(中间件),定义用户请求该路线时服务器行为。

router.get('/', function *(next) {
  this.body = 'Hello World!';
});

上边代码中,router.get主意的首先个参数是根路径,第二个参数是呼应的函数方法。

只顾,路径匹配的时候,不会把询问字符串考虑在内。比如,/index?param=xyz合作路径/index

有个别路径形式比较复杂,Koa-router允许为路径方式起别称。起名时,别称要足够为动词方法的首先个参数,那时动词方法成为接受多个参数。

router.get('user', '/users/:id', function *(next) {
 // ...
});

地方代码中,路径形式\users\:id的名字就是user。路径的称号,能够用来引用对应的现实性路径,比如url方法能够依据路径名称,结合给定的参数,生成具体的路子。

router.url('user', 3);
// => "/users/3"

router.url('user', { id: 3 });
// => "/users/3"

地方代码中,user便是途径方式的称呼,对应具体路径/users/:id。url方法的第三个参数3,表示给定id的值是3,由此最后生成的不二法门是/users/3

Koa-router允许为路径统一添加前缀。

var router = new Router({
  prefix: '/users'
});

router.get('/', ...); // 等同于"/users"
router.get('/:id', ...); // 等同于"/users/:id"

路线的参数通过this.params质量获取,该属性再次回到叁个对象,全数途径参数都以该指标的分子。

// 访问 /programming/how-to-node
router.get('/:category/:title', function *(next) {
  console.log(this.params);
  // => { category: 'programming', title: 'how-to-node' }
});

param方法可以针对命名参数,设置验证条件。

router
  .get('/users/:user', function *(next) {
    this.body = this.user;
  })
  .param('user', function *(id, next) {
    var users = [ '0号用户', '1号用户', '2号用户'];
    this.user = users[id];
    if (!this.user) return this.status = 404;
    yield next;
  })

地点代码中,假使/users/:user的参数user对应的不是有效率户(比如访问/users/3),param方法注册的中间件会查到,就会回来404不当。

redirect方法会将某些路径的伏乞,重定向到另3个路子,并回到301状态码。

router.redirect('/login', 'sign-in');

// 等同于
router.all('/login', function *() {
  this.redirect('/sign-in');
  this.status = 301;
});

redirect方法的率先个参数是呼吁来源,第二个参数是指标地,两者都足以用路径情势的小名代替。

  那样大家就足以去生成大家的数据库, 

context对象

中间件个中的this表示上下文对象context,代表3次HTTP请求和应对,即一次访问/回应的兼具消息,都足以从上下文对象得到。context对象封装了request和response对象,并且提供了有些援助方法。每一回HTTP请求,就会创立3个新的context对象。

app.use(function *(){
  this; // is the Context
  this.request; // is a koa Request
  this.response; // is a koa Response
});

context对象的众多主意,其实是概念在ctx.request对象或ctx.response对象方面,比如,ctx.type和ctx.length对应于ctx.response.type和ctx.response.length,ctx.path和ctx.method对应于ctx.request.path和ctx.request.method。

context对象的全局属性。

  • request:指向Request对象
  • response:指向Response对象
  • req:指向Node的request对象
  • res:指向Node的response对象
  • app:指向App对象
  • state:用于在中间件传递新闻。

    this.state.user = yield User.find(id);

上边代码中,user属性存放在this.state目的方面,能够被另1在那之中间件读取。

context对象的全局方法。

  • throw():抛出错误,直接控制了HTTP回应的状态码。
  • assert():假设三个表达式为false,则抛出一个不当。

    this.throw(403);
    this.throw(‘name required’, 400);
    this.throw(‘something exploded’);

    this.throw(400, ‘name required’);
    // 等同于
    var err = new Error(‘name required’);
    err.status = 400;
    throw err;

assert方法的事例。

// 格式
ctx.assert(value, [msg], [status], [properties])

// 例子
this.assert(this.user, 401, 'User not found. Please login!');

以下模块解析POST请求的数据。

界面来吗正是前段界面包车型地铁编写制定,和后端处理函数的编排,那里吧,作者就不平等叙述了,那里呢
给大家上边包车型客车多少个地点的代码片段,显示下,首假如上传和下载这一块。

错误处理机制

Koa提供放置的错误处理机制,任何中间件抛出的荒唐都会被捕捉到,引发向客户端重临2个500不当,而不会导致进度结束,由此也就不必要forever那样的模块重启进度。

app.use(function *() {
  throw new Error();
});

地方代码中,中间件内部抛出2个荒唐,并不会造成Koa应用挂掉。Koa内置的错误处理机制,会捕捉到那些荒唐。

理所当然,也得以附加布署本身的错误处理机制。

app.use(function *() {
  try {
    yield saveResults();
  } catch (err) {
    this.throw(400, '数据无效');
  }
});

下面代码自行安顿了try…catch代码块,一旦爆发错误,就用this.throw主意抛出。该情势能够将钦点的状态码和错误新闻,再次来到给客户端。

对于未捕获错误,能够设置error事件的监听函数。

app.on('error', function(err){
  log.error('server error', err);
});

error事件的监听函数还能接受上下文对象,作为第二个参数。

app.on('error', function(err, ctx){
  log.error('server error', err, ctx);
});

比方三个荒唐没有被破获,koa会向客户端再次回到三个500破绽百出“Internal Server
Error”。

this.throw方法用于向客户端抛出二个张冠李戴。

this.throw(403);
this.throw('name required', 400);
this.throw(400, 'name required');
this.throw('something exploded');

this.throw('name required', 400)
// 等同于
var err = new Error('name required');
err.status = 400;
throw err;

this.throw主意的七个参数,三个是错误码,另三个是报错音信。假设省略状态码,暗中同意是500张冠李戴。

this.assert主意用于在中间件之中断言,用法类似于Node的assert模块。

this.assert(this.user, 401, 'User not found. Please login!');

地点代码中,假如this.user属性不存在,会抛出多个401不当。

鉴于中间件是层级式调用,所以能够把try { yield next }正是第二在那之中间件。

app.use(function *(next) {
  try {
    yield next;
  } catch (err) {
    this.status = err.status || 500;
    this.body = err.message;
    this.app.emit('error', err, this);
  }
});

app.use(function *(next) {
  throw new Error('some error');
})

重点是测试日志的下载。

CSRF攻击

CS安德拉F攻击是指用户的session被勒迫,用来伪造用户的口诛笔伐。

koa-csrf插件用来严防CS昂科雷F攻击。原理是在session之中写入3个地下的token,用户每趟使用POST方法提交数据的时候,必须带有这么些token,不然就会抛出荒谬。

 1 var koa = require('koa');
 2 var session = require('koa-session');
 3 var csrf = require('koa-csrf');
 4 var route = require('koa-route');
 5 
 6 var app = module.exports = koa();
 7 
 8 app.keys = ['session key', 'csrf example'];
 9 app.use(session(app));
10 
11 app.use(csrf());
12 
13 app.use(route.get('/token', token));
14 app.use(route.post('/post', post));
15 
16 function* token () {
17   this.body = this.csrf;
18 }
19 
20 function* post() {
21   this.body = {ok: true};
22 }
23 
24 app.listen(3000);

POST请求含有token,能够是以下两种艺术之一,koa-csrf插件就能收获token。

  • 表单的_csrf字段
  • 查询字符串的_csrf字段
  • HTTP请求头新闻的x-csrf-token字段
  • HTTP请求头消息的x-xsrf-token字段 
@app.route('/load/<string:filename>',methods=['GET'])
def load(filename):
    basedir = os.path.abspath(os.path.dirname(__file__))
    file_dir=os.path.join(basedir,'upload')
    response=make_response(send_from_directory(file_dir,filename,as_attachment=True))
    return response

 

单个用例的实施吗,笔者那边没有变化测试报告,作者呢这一个根本采取一个用例的检察上去,

三个用来啊,作者那边用的是前边作者在8地点提到的测试框架,在此间小编做了一部分修改,大的尚未怎么转移,只是对部分代码实行优化。

接下去就是给我们体现下效果图

1.那是体系的目录

图片 2

图片 3

图片 4图片 5图片 6图片 7图片 8

 

 

到此地呢,小编的主导的测试框架就早已解决了。前前后后累计大致开支了本身多个周的小时吗,第一个demo版本就这么横空出世了。

本身选择布署的是Ali云,

http://60.205.187.178:89      

http://www.lileilei.online:89/

透过地点的地址能够访问到。

以此周末东京的气象还算晴朗,天也不热,写起来代码也是六六的。

 

有毛病可以加笔者qq:952943386可能作者的qq群一九四八04520

瞩望大家一同冲菜鸟飞到更高

有钱的也足以给本人捧下。

 

 

 

别的小编提供vip指点,python自动化,python学习地点,python做测试开发方面,小编都会提供引导,vip平生有效,现价1500,联系qq:952943386,稍后会更新qq群,喜欢的敌人能够来,本身也是从三个不懂吗是python到前日能做东西,从15的2k到17的10k+都是辛酸泪,能够与大家一块儿共勉,提供思路,能够让您走的更远,笔者也是从白纸一施晓东步步走来,假使您是新结束学业的学习者那些也契合您,能够给你指点,可是本身能做的便是带您,可是小编不是培养和演练班,没有提供就业的时机,笔者只会在有机遇的时候给您推荐,学无边无际,此时大概小编只得教你有些测试,提供职业引导,我大概以往会带给您越多。

 

本人是二个大专生,也是一人发奋到后天。

 

图片 9

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图