利用python 构建一个APP 模拟测试环境

背景

做app开发的,和后台连调是一个很重要的工作。在我来到这家公司之前,一只以为所有的功能是后台先开发出来然后app才开始开发。但是由于现在app开发和后台开发是两套人马,所以不可避免的需要同时开发甚至同时上线。我们不得不面临的一个问题是后台只给出了接口,客户端并没有可以连调的环境,这样就不得不自己写虚拟数据模拟。让客户端能够按某个流程能够点下去。

解决方法

  1. 在我接手钱盒的时候,很多地方有一个全局静态flag,好像叫一个走马观花模式。开启这个flag,可以不与后台交互,而直接一步一步走界面。这种方法固然比较好,但是这个修改了代码的流程,在真实环境下用不到这个逻辑,而且容易给app测试埋坑。
  2. 利用mock技术模拟http返回,这虽然有很多框架可以做到,但是对于新人而言,学习成本较高,不同平台可能有不同的解决方案。因此也不是解决连调问题的首选。
  3. 写http服务模拟数据返回。这是一种兼容多个平台的方案,android ios 以及h5 都能够适配的通用解决方案。下面分析一下如何实现

实现

做过servlet 的人都知道,servlet是一个基于java的http服务器的拓展,类似于cgi(common gateway interface)。一个servlet实现类需要一般来说要实现doGet doPost,用来处理客户端发送过来的数据。处理完成之后通过response返回数据。这样我们就可以动态的给客户端返回数据了。但是servlet程序是需要部署到server容器里面。比如tomcat才能执行,要装java运行环境。而且java代码是相对比较繁琐。因此我们用另外一种可以飞起来的语言python来实现。只需要python环境即可,python标准环境就包含了http相关的类库

我们将返回的数据写死在代码里面显然是不科学的。因为不可能我们要改一个返回就得修改代码,并重新运行,采用文件保存返回内容是一个很好的解决方法,然后通过不同的请求路径去获取相应的文件。没有找到相应文件就创建相应文件。

于是开始网上查询 https://wiki.python.org/moin/BaseHttpServer 只需实现BaseHttpServer就能实现一个简单的http请求处理程序。

import time
import BaseHTTPServer


HOST_NAME = 'example.net' # !!!REMEMBER TO CHANGE THIS!!!
PORT_NUMBER = 80 # Maybe set this to 9000.


class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_HEAD(s):
        s.send_response(200)
        s.send_header("Content-type", "text/html")
        s.end_headers()
    def do_GET(s):
        """Respond to a GET request."""
        s.send_response(200)
        s.send_header("Content-type", "text/html")
        s.end_headers()
        s.wfile.write("<html><head><title>Title goes here.</title></head>")
        s.wfile.write("<body><p>This is a test.</p>")
        # If someone went to "http://something.somewhere.net/foo/bar/",
        # then s.path equals "/foo/bar/".
        s.wfile.write("<p>You accessed path: %s</p>" % s.path)
        s.wfile.write("</body></html>")

if __name__ == '__main__':
    server_class = BaseHTTPServer.HTTPServer
    httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)
    print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()
    print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)

整个代码就35行。在此基础上参阅了一些相关文档,我重新写了do_POST方法,可以创建和读取文件
一个简单的模拟返回app 就搭建好了 代码如下

#!/usr/bin/python
# coding=utf-8
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
"""
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import os
import cgi
import time
import codecs
import sys

reload(sys)
sys.setdefaultencoding("utf-8")
print sys.getdefaultencoding()

PORT_NUMBER = 8888
RES_FILE_DIR = "."


class myHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            self.path = "/index.html"

        try:
            # 根据请求的文件扩展名,设置正确的mime类型
            sendReply = False
            if self.path.endswith(".html"):
                mimetype = 'text/html'
                sendReply = True
            if self.path.endswith(".jpg"):
                mimetype = 'image/jpg'
                sendReply = True
            if self.path.endswith(".gif"):
                mimetype = 'image/gif'
                sendReply = True
            if self.path.endswith(".js"):
                mimetype = 'application/javascript'
                sendReply = True
            if self.path.endswith(".css"):
                mimetype = 'text/css'
                sendReply = True

            if sendReply == True:
                # 读取相应的静态资源文件,并发送它
                f = open(os.curdir + os.sep + self.path, 'rb')
                self.send_response(200)
                self.send_header('Content-type', mimetype)
                self.end_headers()
                self.wfile.write(f.read())
                f.close()
            return

        except IOError:
            self.send_error(404, 'File Not Found: %s' % self.path)

    def do_POST(self):
        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD': 'POST',
                     'CONTENT_TYPE': self.headers['Content-Type'],
            })
        print self.path
        keys = form.keys()
        params = ""
        for key in keys:
            params += key + "=" + form.getvalue(key, "") + "\n"
        print params[:len(params) - 1]

        # 读取相应的静态资源文件,并发送它
        full_file_path = os.curdir + os.sep + self.path
        if os.path.isfile(full_file_path):
            file_out = open(full_file_path, 'rb')
            full_text = file_out.read()
            file_out.close()
            self.send_response(200)
            self.end_headers()
            if len(full_text) > 0:
                self.wfile.write(full_text)
                print(full_text)
            else:
                self.wfile.write(u"{\"status\":1,\"remark\":\"请求处理不存在\"}")
        else:
            self.send_response(200)
            self.end_headers()
            retstr = u"{\"status\":-1,\"remark\":\"请求处理不存在,新建处理\"}"
            self.wfile.write(retstr)
            if not os.path.exists(os.path.split(full_file_path)[0]):
                os.makedirs(os.path.split(full_file_path)[0])
            fwrite = codecs.open(full_file_path, 'wb', 'utf-8')
            fwrite.write(retstr)
            fwrite.close()
            print retstr, "已保存到文件,路径:", full_file_path


try:
    server = HTTPServer(('', PORT_NUMBER), myHandler)
    print 'Started httpserver on port ', PORT_NUMBER

    server.serve_forever()

except KeyboardInterrupt:
    print '^C received, shutting down the web server'
    server.socket.close()

Leave a Reply

Your email address will not be published. Required fields are marked *