效率利器——从Api自动生成Jmeter测试脚本

364 阅读3分钟

文章结构

前言:不会用Jmeter的测试工程师不是一个好的程序员。自己码的api,交给前端开发的时候,你心里有点谱吗? JMeter是神马啊? Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。相比Loadrunner而言,JMeter小巧轻便且免费,逐渐成为了主流的性能测试工具,是每个测试人员都必须要掌握的工具之一。

在这里插入图片描述

Jmeter载入结果

在这里插入图片描述
先上结果,快速生成的Jmeter测试例程其实是没有正确的数据的,只是随机生成的数据,但是结构是正确的。因此,你需要调整下使用的测试数据以及测试的顺序,然后就可以happy的玩了,玩完记得保存,下次如果有api的变动,仍然可以继续验证改变的影响,前端的问题也可以快速的反馈了。

生成Jmeter测试计划

打开一个Jmeter测试计划,发现就是xml,如下所示。

在这里插入图片描述
xmlwriter来支持写xml

/// <summary>
        /// Writes &lt;'elementName' name='name' elementType='elementType'&gt;
        /// </summary>
        internal static void WriteStartElement(XmlWriter xmlWriter, string elementName, string name, string elementType)
        {
            xmlWriter.WriteStartElement(elementName);
            xmlWriter.WriteAttributeString("name", name);
            xmlWriter.WriteAttributeString("elementType", elementType);
        }

        /// <summary>
        /// Writes &lt;'elementName' name='name'&gt;
        /// </summary>
        internal static void WriteStartElement(XmlWriter xmlWriter, string elementName, string name)
        {
            xmlWriter.WriteStartElement(elementName);
            xmlWriter.WriteAttributeString("name", name);
        }

        /// <summary>
        /// Writes &lt;'elementName' name='name'&gt;&lt;/'elementName'&gt;
        /// </summary>
        internal static void WriteStartAndEndElement(XmlWriter xmlWriter, string elementName, string name)
        {
            xmlWriter.WriteStartElement(elementName);
            xmlWriter.WriteAttributeString("name", name);
            xmlWriter.WriteEndElement();
        }

        /// <summary>
        /// Writes &lt;'elementName'&gt;'value'&lt;/'elementName'&gt;
        /// </summary>
        internal static void WriteSimpleElement(XmlWriter xmlWriter, string elementName, string elementValue)
        {
            xmlWriter.WriteStartElement(elementName);
            xmlWriter.WriteString(elementValue);
            xmlWriter.WriteEndElement();
        }

        /// <summary>
        /// Writes &lt;'elementName' name='valueOfPropName'&gt;'elementValue'&lt;/'elementName'&gt;
        /// </summary>
        internal static void WriteElementWithTextChildren(XmlWriter xmlWriter, string elementName, string valueOfPropName,
                                                       string elementValue)
        {
            xmlWriter.WriteStartElement(elementName);
            xmlWriter.WriteAttributeString("name", valueOfPropName);
            xmlWriter.WriteString(elementValue);
            xmlWriter.WriteEndElement();
        }

从Controller提取api

当然自己不全写了,采用 ObjectGenerator来改。这个类应该是微软官方或者swagger的,直接拿来修改用即可。
辅助类如下:

public class ApiHelper
    {
        public static List<TestApi> GetAllControllers(string assembly)
        {
            List<TestApi> apis = new List<TestApi>();
            var asm = Assembly.Load(assembly);
            var ctls = asm.GetTypes().Where(x => x.BaseType == typeof(Controller) || x.IsSubclassOf(typeof(Controller)));

            var list = ctls.ToList();
            foreach (var ctl in ctls)
            {
                var methods = GetAllMetods(ctl);
                var r = GetRoute(ctl);
                foreach (var m in methods)
                {
                    var api = new TestApi();
                    var isPost = IsPost(m);
                    var desc = GetDesc(m);
                    api.Title = desc;
                    api.post = isPost;
                    if (string.IsNullOrEmpty(desc)) continue;
                    var route = GetRoute(m);
                    if (string.IsNullOrEmpty(r))
                    {
                        api.uri = $"api/{ctl.Name.Replace("Controller", "")}/{m.Name}";
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(route))
                        {
                            api.uri = r.Replace("[controller]", ctl.Name.Replace("Controller", "")).Replace("[action]", m.Name);
                        }
                        else
                        {
                            if (r.Contains("[action]"))
                                api.uri = r.Replace("[controller]", ctl.Name.Replace("Controller", "")).Replace("[action]", route);
                            else
                                api.uri = r.Replace("[controller]", ctl.Name.Replace("Controller", "")) + $"/{route}";
                        }
                    }
                    var pas = m.GetParameters();
                    if (isPost)
                    {
                        ObjectGenerator og = new ObjectGenerator();
                        var obj = og.GenerateObject(pas.FirstOrDefault()?.ParameterType ?? typeof(object));
                        //var json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings
                        //{
                        //    Formatting = Formatting.Indented,
                        //});
                        api.inParam = obj;
                    }
                    else
                    {
                        foreach (var p in pas)
                        {
                            if (string.IsNullOrEmpty(api.inParam))
                                api.inParam = $"{p.Name}=";
                            else
                                api.inParam += $"&{p.Name}=";
                        }
                    }

                    apis.Add(api);
                }
            }
            return apis;
        }

        public static List<MethodInfo> GetAllMetods(Type type)
        {
            return type.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.DeclaredOnly |
                                                BindingFlags.Instance).ToList();
        }

        public static bool IsPost(MethodInfo m)
        {
            var cas = m.GetCustomAttributes(false);
            var post = (HttpPostAttribute)(cas.ToList().FirstOrDefault(x => x.GetType() == typeof(HttpPostAttribute)));
            return post != null;
        }
        public static string GetRoute(MethodInfo m)
        {
            var cas = m.GetCustomAttributes(true);
            var route = (RouteAttribute)(cas.ToList().FirstOrDefault(x => x.GetType() == typeof(RouteAttribute)));
            return route?.Template;
        }

        public static string GetDesc(MethodInfo m)
        {
            var cas = m.GetCustomAttributes(true);
            var route = (DisplayAttribute)(cas.ToList().FirstOrDefault(x => x.GetType() == typeof(DisplayAttribute)));
            return route?.Name;
        }
        public static string GetRoute(Type m)
        {
            var cas = m.GetCustomAttributes(true);
            var route = (RouteAttribute)(cas.ToList().FirstOrDefault(x => x.GetType() == typeof(RouteAttribute)));
            return route?.Template;
        }
    }

    public class TestApi
    {
        /// <summary>
        /// 相对路径
        /// </summary>
        public string uri { get; set; }
        public string Title { get; set; }
        public bool post { get; set; }
        public dynamic inParam { get; set; }

        public string test { get; set; }
        public bool testResult { get; set; }
        public string rtnError { get; set; }
    }

生成jmeter

var apis = ApiHelper.GetAllControllers("SCM.AMS.AmsWxApp.APIs");
var jmeter = new JMeterGenerator();
var dir = Assembly.GetExecutingAssembly().Location;
FileInfo fi = new FileInfo(dir);
jmeter.Save(apis, fi.DirectoryName, "abc");

源码可联系作者

源码可以联系我!
这个可以按需自己写,因为并没有很好的抽象出一个工具,因此就不献丑了。