After some attempts, I feel that the ability to apply link pressure measurement in the current project is not waiting for others.

About the link

In fact, the word link is not as easy to understand as the path. It is more efficient to communicate with the product. The specific operation path, the product will give a out, but this is based on THE UI and product thinking document, with the interface test is still very different, can only provide reference basis.

Some business details also need to be supplemented by the test students. I also need my operation and maintenance colleagues to help me figure out the proportion of request volume of each interface. I wrote the proportion this time according to the flash of inspiration and then we adjusted it together.

PS: I prefer to use diagrams now rather than words, because communication is too efficient. The recommended tool is draw.io. For those interested, please refer to the two architecture diagrams in the hot topic at the end of this article.

  • The login was removed this time, because it was too slow and had a big impact on the test results.

Scenario thinking

scenario

The scenario is that the teacher logs in, first requests a list of knowledge points, then filters the list of recommended courses through the attributes of knowledge points, saves and unsaves the data in the list of courses, and obtains the list of courses under his current knowledge points (including original and favorites).

Train of thought

The pressure test model of fixed threads is still adopted this time. I estimate that the threads are about 200, and the test users are 600 in reserve. 2 pages of data are guaranteed for the list page.

Each thread is bound to a user, and then the user starts the loop to execute the steps, each time as a Q. A single Q contains nine HTTP interface requests (the Socket interface is discarded and added to the link as required), including three modification operations and six query operations.

The specific logic is realized by internal static class, and then there is a K class, which is used to store the knowledge point properties each time, and it is convenient to call. Because the interface request methods take the underlying data type and String as arguments, they can be a bit verbose when called. But it doesn’t matter, the script is written, is used up and then thrown into the warehouse, another day to use and optimize.

The Demo implementation

package com.okayqa.composer.performance.resource1_4

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.funtester.base.bean.AbstractBean
import com.funtester.base.constaint.ThreadLimitTimesCount
import com.funtester.frame.execute.Concurrent
import com.funtester.httpclient.ClientManage
import com.funtester.utils.ArgsUtil
import com.okayqa.composer.base.OkayBase
import com.okayqa.composer.function.Mirro
import com.okayqa.composer.function.OKClass

class Login_collect_uncollect extends OkayBase {

    public static void main(String[] args) {
        ClientManage.init(10.5.0."".0)
        def util = new ArgsUtil(args)
        def thread = util.getIntOrdefault(0.30)
        def times = util.getIntOrdefault(1.40)

        def tasks = []

        thread.times {
            tasks << new FunTester(it, times)
        }

        new Concurrent(tasks, "Resource Library 1.4 Login > Query > Favorites > Unfavorites Link Pressure Test").start()

        allOver()
    }

    private static class FunTester extends ThreadLimitTimesCount<Integer> {

        OkayBase base

        def mirro

        def clazz

        FunTester(Integer integer, int times) {
            super(integer, times, null)}@Override
        void before(a) {
            super.before()
            base = getBase(t)
            mirro = new Mirro(base)
            clazz = new OKClass(base)
        }

        @Override
        protected void doing(a) throws Exception {

            def klist = mirro.getKList()
            def karray = klist.getJSONArray("data")
            K ks
            karray.each {
                JSONObject parse = JSON.parse(JSON.toJSONString(it))
                if (ks == null) {
                    def level = parse.getIntValue("node_level")
                    def type = parse.getIntValue("ktype")
                    def id = parse.getIntValue("id")
                    ks = new K(id, type, level)
                }
            }
            JSONObject response = clazz.recommend(ks.id, ks.type, ks.level)
            def minis = []
            int i = 0
            response.getJSONArray("data").each {
                if (i++ < 2) {
                    JSONObject parse = JSON.parse(JSON.toJSONString(it))
                    int value = parse.getIntValue("minicourse_id")
                    minis << value
                }
            }
            clazz.unCollect(random(minis))

            mirro.getMiniCourseListV3(ks.id, ks.type, 0, ks.level)
        }
    }

    private static class K extends AbstractBean {

        int id

        int type

        int level

        K(int id, int type, int level) {
            this.id = id
            this.type = type
            this.level = level
        }
    }

}


Copy the code

AbstractBean class AbstractBean class AbstractBean class AbstractBean class AbstractBean class AbstractBean class AbstractBean class

package com.funtester.base.bean

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.funtester.frame.Save
import com.funtester.frame.SourceCode
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.BeanUtils

/** * the base class of bean */
abstract class AbstractBean {

    static final Logger logger = LoggerFactory.getLogger(AbstractBean.class)

    /** * Convert the bean to JSON for data processing and printing@return* /
    JSONObject toJson(a) {
        JSONObject.parseObject(JSONObject.toJSONString(this))}/** * Save */ as text
    def save(a) {
        Save.saveJson(this.toJson(), this.getClass().toString() + SourceCode.getMark());
    }

    /** * Console prints and logs with WARN for viewing */
    def print(a) {
        logger.warn(this.getClass().toString() + ":" + this.toString());
    }

    def initFrom(String str) {
        JSONObject.parseObject(str, this.getClass())
    }

    def initFrom(Object str) {
        initFrom(JSON.toJSONString(str))
    }

    def copyFrom(AbstractBean source) {
        BeanUtils.copyProperties(source, this)}def copyTo(AbstractBean target) {
        BeanUtils.copyProperties(this, target)
    }

    /** * The bean properties must be accessible, otherwise an empty JSON string * will be returned@return* /
    @Override
    String toString(a) {
        JSONObject.toJSONString(this)}@Override
    protected Object clone(a) {
        initFrom(this)}}Copy the code

Console output

~ ☢ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ~ JSON ~ ☢ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ~ > {> (1)."rt":1665> 1)."total":1188> 1)."qps":18.018> 1)."failRate":0.0> 1)."threads":30> 1)."startTime":"The 2021-02-24 16:57:23"> 1)."endTime":"The 2021-02-24 16:58:34"> 1)."errorRate":1.01> 1)."executeTotal":1188> 1)."mark":"Resource Library 1.4 Login > Query > Favorites > Unfavorites Link Pressure test 241657"> 1)."table":"eJzj5VLAD15sbXm2a8LTXZMN9Uyez9z9dO9Uu2fzl75Yv8ju2ZRtL6b32z3tn/ZsWweE83Lyvhfb1z/t6362tZvT2EChJKMoNaWYgA0KvFy8+F0RlFpckJ9 XnKoQkpmbaqVQoVucWpSZmKOQV5qro1Cpm5uakpmYR8gOQq5QyM3MU4AYZWVhYqmQW6yTm1hhZWxoaQxkE9RNjA2UgEfTOoBo1JZRW2hmRSsQ0ccmsBW0tgn VQzS1DatVtLMRn3W0sPXRtCYgAlLtQITXWura/mhaMxCRYC+VXUGyv2nhmkfTGoGI0rCgrsseTWsBImLSIZ1dCA8sOjmMXNfCU9ZAupNYV8MdC4106qdA2vk AniAGq6OJ8AlVi6GB99FgSvTU8BU8egaBg6jsO3iWHwSuoZUPB4EzRn046sNRHw68M0Z9OOrDEe5DABkr1eo=">} ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ~ JSON ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~Copy the code


FunTester.Authorized author of the Year by Tencent Cloud Community, non-famous test developer ER, welcome to follow.

  • FunTester test framework architecture diagram
  • FunTester test project architecture diagram
  • 2020 FunTester self-summary
  • Functional and non-functional testing
  • What happens if you have 100 years of membership
  • Asynchronous query to synchronous and redis business BUG sharing
  • Two common concurrency errors on the Java server
  • How does the command line execute methods in jar packages
  • The maximum number of consecutive occurrences in a crooked string
  • There is no data to drive automated tests
  • How does automation select use cases
  • Advantages of successfully implementing automated tests