Python中如何将Flask markdown格式的API文档转换为API与测试信息的mini轮子

在写好格式要求的 markdown 文档的前提下可以省不少力气写这些 api 文档的固定部分,包括 method, url arguments, post/put data, response data 还有 test 等等.仅适用于前后端分离.

使用场景有点局限,希望不被喷得太惨....

Github->: DocTrans

效果: doctrans.gif


Python中如何将Flask markdown格式的API文档转换为API与测试信息的mini轮子

1 回复

我理解你想做一个能把Flask的Markdown格式API文档转换成可执行测试的小工具。这个需求很实用,我来给你一个可以直接用的方案。

核心思路是解析Markdown中的API信息(URL、方法、参数),然后生成对应的测试代码。下面是一个完整的实现:

import re
import json
import requests
from typing import Dict, List, Optional
import markdown
from bs4 import BeautifulSoup

class FlaskMarkdownToTester:
    def __init__(self, markdown_file: str):
        self.markdown_file = markdown_file
        self.apis = []
        
    def parse_markdown(self):
        """解析Markdown文件,提取API信息"""
        with open(self.markdown_file, 'r', encoding='utf-8') as f:
            content = f.read()
        
        # 将markdown转换为HTML便于解析
        html = markdown.markdown(content)
        soup = BeautifulSoup(html, 'html.parser')
        
        # 查找所有标题(假设API以##开头)
        for h2 in soup.find_all('h2'):
            api_name = h2.get_text().strip()
            api_info = {'name': api_name, 'method': 'GET', 'url': '', 'params': {}}
            
            # 查找下一个段落或代码块
            next_elem = h2.find_next_sibling()
            while next_elem and next_elem.name != 'h2':
                if next_elem.name == 'p':
                    text = next_elem.get_text()
                    # 提取URL和方法
                    if 'POST' in text or 'GET' in text or 'PUT' in text or 'DELETE' in text:
                        method_match = re.search(r'(GET|POST|PUT|DELETE|PATCH)', text)
                        if method_match:
                            api_info['method'] = method_match.group(1)
                        
                        url_match = re.search(r'`(/api/[^`]+)`', text)
                        if url_match:
                            api_info['url'] = url_match.group(1)
                
                elif next_elem.name == 'pre':
                    code = next_elem.get_text()
                    # 尝试解析JSON参数示例
                    if '{' in code and '}' in code:
                        try:
                            json_start = code.find('{')
                            json_end = code.rfind('}') + 1
                            json_str = code[json_start:json_end]
                            params = json.loads(json_str)
                            api_info['params'] = params
                        except:
                            pass
                
                next_elem = next_elem.find_next_sibling()
            
            if api_info['url']:  # 只有找到URL的才加入
                self.apis.append(api_info)
        
        return self.apis
    
    def generate_test_code(self, base_url: str = "http://localhost:5000") -> str:
        """生成测试代码"""
        if not self.apis:
            self.parse_markdown()
        
        test_code = """import unittest
import requests
import json

class TestFlaskAPIs(unittest.TestCase):
    def setUp(self):
        self.base_url = "{}"
        self.session = requests.Session()
        # 如果需要认证,在这里添加
        # self.session.headers.update({'Authorization': 'Bearer token'})
        
""".format(base_url)
        
        for i, api in enumerate(self.apis):
            test_name = f"test_{api['name'].lower().replace(' ', '_')}"
            method = api['method'].lower()
            url = api['url']
            params = json.dumps(api['params'], ensure_ascii=False, indent=2)
            
            test_method = f"""
    def {test_name}(self):
        \"\"\"测试 {api['name']}\"\"\"
        url = self.base_url + "{url}"
        
        response = self.session.{method}(
            url,
            json={params} if {bool(api['params'])} else None
        )
        
        print(f"{{api['method']}} {{url}} - 状态码: {{response.status_code}}")
        self.assertEqual(response.status_code, 200)
        # 这里可以添加更多的断言
        # self.assertIn('key', response.json())
        
"""
            test_code += test_method
        
        test_code += """
if __name__ == '__main__':
    unittest.main()
"""
        return test_code
    
    def save_test_file(self, filename: str = "test_apis.py", base_url: str = "http://localhost:5000"):
        """保存测试文件"""
        test_code = self.generate_test_code(base_url)
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(test_code)
        print(f"测试文件已生成: {filename}")

# 使用示例
if __name__ == "__main__":
    # 假设你的Markdown文档文件名为api_docs.md
    converter = FlaskMarkdownToTester("api_docs.md")
    
    # 解析Markdown
    apis = converter.parse_markdown()
    print(f"找到 {len(apis)} 个API")
    for api in apis:
        print(f"{api['method']} {api['url']} - {api['name']}")
    
    # 生成测试文件
    converter.save_test_file("test_generated.py", "http://localhost:5000")

这个工具的工作原理:

  1. 用BeautifulSoup解析Markdown转换的HTML
  2. 通过正则表达式提取API方法、URL和参数
  3. 生成完整的unittest测试用例
  4. 可以直接运行生成的测试文件

如果你的Markdown格式比较特殊,可能需要调整正则表达式。这个方案给你一个基础框架,你可以根据实际文档格式进行定制。

总结:用解析+代码生成的方式实现文档到测试的转换。

回到顶部