• pytest学习笔记


    1、直接在anaconda prompt运行pytest(前提是已经安装pytest,也可以用colab

    2、若要使用–json-report等argument,需要安装pytest插件

    pip install pytest-json-report --upgrade 
    
    • 1

    3、若要使用–suppress-tests-failed-exit-code

    !pip install pytest-custom-exit-code
    
    • 1

    4、若要使用jq,需要安装

    以colab为例,colab用的是Ubuntu 18.04.6 LTS (Bionic Beaver)容器

    !cat /etc/os-release
    
    • 1

    在Ubuntu下可以使用如下命令安装

    !apt-get install jq
    
    • 1

    如果要安装的是pyjq,则需要先安装autoconf libtool

    !apt-get install autoconf libtool
    
    • 1

    然后安装pyjq

    !pip install pyjq
    
    • 1

    案例

    将测试写入.sh文件,可以看到里面用了各种arguments以及jq工具

    #!/bin/bash
    
    OUTPUT=/tmp/hw1p1.log
    REPORT=report.json
    
    PYTEST_OPTS=""
    PYTEST_OPTS="$PYTEST_OPTS --cache-clear -rA --capture=no --show-capture=no --tb=short"
    PYTEST_OPTS="$PYTEST_OPTS --json-report --json-report-file=$REPORT --json-report-omit collectors keywords"
    PYTEST_OPTS="$PYTEST_OPTS --suppress-tests-failed-exit-code"
    
    start=`date +%s`
    pytest $PYTEST_OPTS . > "$OUTPUT" 2>&1
    #python3 -u hw1p1-soln.py 
    status=$?
    end=`date +%s`
    
    echo "Runtime: $((end-start))"
    cat $OUTPUT
    
    # checkout output status
    if [ ${status} -ne 0 ]; then
        echo "Failure: fails or returns nonzero exit status of ${status}"
        echo '{"scores": {"Success": 0}}'
        exit
    fi
    
    # include any post-parsing here
    
    NFAIL=$(jq -r '.summary.failed | select(.!=null)' < report.json)
    NPASS=$(jq -r '.summary.passed | select(.!=null)' < report.json)
    NTOTAL=$(jq -r '.summary.total | select(.!=null)' < report.json)
    
    echo '{"scores": {"Success":1, "Pass":'${NPASS:=0}', "Fail":'${NFAIL:=0}', "Score":'$((100 * $NPASS/${NTOTAL:=1}))'}}'
    
    exit
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    辅助文件 test_hw1p1.py

    import subprocess, string
    import pytest
    
    EXEC='python3'
    SCRIPT_TO_TEST='./hw1p1.py'
    
    # PROBLEM_ID: STRING
    STRINGS_TO_TEST = [
      'string',
      'space string',
      'stringstring',
      'stringstringstring',
      'aaaaaaaaaaaaaaaaaa',
      'foo1bar',
      '',
      'Loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliqua',
      '.',
      ' ',
      'invalid^'
    ]
    
    EXPECT_RETURNCODE = 0
    
    
    # only supports text stdout/stderr
    def run_test_script(args):
      # check args list
      if not isinstance(args, list):
        args = [args]
      run = [EXEC, SCRIPT_TO_TEST] + args
    
      # start script
      ps = subprocess.Popen(run, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
      ps.wait()
    
      [stdout, stderr] = [x.decode('utf-8').strip() for x in ps.communicate()]
      returncode = int(ps.returncode)
    
      return (returncode, stdout, stderr)
    
    
    # r = k!
    def factorial(k):
      r = 1
      for i in range(1, k+1):
        r *= i
      return r
    
    
    def count_anagrams(s):
      # normalize
      s = s.lower()
    
      # "histogram"
      hist = [0] * 26
      for k, letter in enumerate(string.ascii_lowercase):
        hist[k] = s.count(letter)
    
      # multinomial log-implementation
      perm1 = factorial(sum(hist))
      # note, long strings need arbitrary precision
      # published first without, so return both and count either correct
      perm2 = perm1
    
      for count in hist:
        perm1 /= factorial(count)
        perm2 //= factorial(count)
    
      return [int(perm1), int(perm2)]
    
    
    def is_valid(s):
      return not s or s.isalpha()
    
    # does not check valid, only anagram count => number / empty
    def assert_stdout(s, stdout):
      if not is_valid(s):
        assert stdout == '', f'string "{s}" STDOUT: expected=, actual={stdout}'
      elif len(s) == 0:
        assert stdout == 'empty', f'string "{s}" STDOUT: expected=empty, actual={stdout}'
      else:
        # note, see comment above
        count1, count2 = count_anagrams(s)
        assert stdout == f'{count1}' or stdout == f'{count2}'
    
    def assert_stderr(s, stderr):
      if not is_valid(s):
        assert stderr == 'invalid', f'string "{s}" STDERR: expected=invalid, actual={stderr}'
      else:
        assert stderr == '', f'string "{s}" STDERR: expected=, actual={stderr}'
    
    
    @pytest.mark.parametrize('s', [s for s in STRINGS_TO_TEST])
    def test_string(s):
      returncode, stdout, stderr = run_test_script(s)
    
      # if return code does not match AND == 1, assume program crashed, return error
      if returncode != EXPECT_RETURNCODE and returncode == 1:
        raise Exception(stderr)
    
      assert returncode == EXPECT_RETURNCODE, f'string "{s}" returncode:expected={EXPECT_RETURNCODE}, actual={returncode}'
      assert_stderr(s, stderr)
      assert_stdout(s, stdout)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    report.json

    {"created": 1662063651.473876, "duration": 0.5840983390808105, "exitcode": 0, "root": "/content", "environment": {"Python": "3.7.13", "Platform": "Linux-5.4.188+-x86_64-with-Ubuntu-18.04-bionic", "Packages": {"pytest": "7.1.2", "py": "1.11.0", "pluggy": "1.0.0"}, "Plugins": {"json-report": "1.5.0", "custom-exit-code": "0.3.0", "metadata": "2.0.2", "typeguard": "2.7.1"}}, "summary": {"passed": 11, "total": 11, "collected": 11}, "tests": [{"nodeid": "test_hw1p1.py::test_string[string]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.0003476919999911843, "outcome": "passed"}, "call": {"duration": 0.04421803299999283, "outcome": "passed"}, "teardown": {"duration": 0.0002795460000015737, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[space string]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.00032319900003585644, "outcome": "passed"}, "call": {"duration": 0.041243673000053604, "outcome": "passed"}, "teardown": {"duration": 0.00024495399998158973, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[stringstring]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.00033566100000825827, "outcome": "passed"}, "call": {"duration": 0.04028028899995206, "outcome": "passed"}, "teardown": {"duration": 0.00024724699994749244, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[stringstringstring]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.000330002000055174, "outcome": "passed"}, "call": {"duration": 0.0426553290000129, "outcome": "passed"}, "teardown": {"duration": 0.00026272699994933646, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[aaaaaaaaaaaaaaaaaa]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.0003462529999751496, "outcome": "passed"}, "call": {"duration": 0.04282293200003551, "outcome": "passed"}, "teardown": {"duration": 0.0002480279999872437, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[foo1bar]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.00032949400008419616, "outcome": "passed"}, "call": {"duration": 0.04206459699992138, "outcome": "passed"}, "teardown": {"duration": 0.00026792599999225786, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.0003373019999344251, "outcome": "passed"}, "call": {"duration": 0.0413927589999048, "outcome": "passed"}, "teardown": {"duration": 0.00025381800003287935, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[Loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliqua]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.000335223999968548, "outcome": "passed"}, "call": {"duration": 0.04252076099999158, "outcome": "passed"}, "teardown": {"duration": 0.00024685599998974794, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[.]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.0003379639999820938, "outcome": "passed"}, "call": {"duration": 0.04881728899999871, "outcome": "passed"}, "teardown": {"duration": 0.00025473300001976895, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[ ]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.00035387100001571525, "outcome": "passed"}, "call": {"duration": 0.043154117000085535, "outcome": "passed"}, "teardown": {"duration": 0.0002428340000051321, "outcome": "passed"}}, {"nodeid": "test_hw1p1.py::test_string[invalid^]", "lineno": 92, "outcome": "passed", "setup": {"duration": 0.0003739240000868449, "outcome": "passed"}, "call": {"duration": 0.04142785100009405, "outcome": "passed"}, "teardown": {"duration": 0.00030338299995946727, "outcome": "passed"}}]}
    
    • 1

    被测试代码 hw1p1.py

    import sys
    
    def isalpha(s):
        len_ = len(s)
        isletter = lambda ch: True if 97<=ord(ch)<=122 else False
        if sum(list(map(isletter, s))) == len_:
            return True
        return False
    
    def validate():
        # check if empty
        try:
            # check input string is there
            assert len(sys.argv) > 1
            s = sys.argv[1].lower()
            assert len(s)
        except:
            sys.stdout.write("empty")
            sys.exit()
        # check if valid format
        try:
            s = sys.argv[1].lower()
            assert isalpha(s)
        except:
            sys.stderr.write("invalid")
            sys.exit()
    
    def factorial(n):
        return 1 if n == 0 or n == 1 else n*factorial(n-1)
        
    def combination(n, r):
        return factorial(n)//(factorial(r)*factorial(n-r))
    
    def permutation(n, r):
        return factorial(n)//factorial(n-r)
            
    def count_unique_anagram(s: str) -> int:
        # start from the smallest anagram
        s = sorted(s)
    
        # accumulate count, and initialize room for rearrangement
        count, room = 1, len(s)
        # count letters
        ch2count = [0]*26 # 0: a, 1: b, ..., 25: z
        for ch in s:
            ch2count[ord(ch)-97] += 1
        # sort for combination first
        ch2count.sort(reverse=True)
        
        num_of_1 = 0
        for i, c in enumerate(ch2count):
            if c > 1: # combination first for letters that appears multiple
                count *= combination(room, c)
                room -= c
            elif c > 0: # count number of letter that only appears once
                num_of_1 += 1
        
        # do permuatation
        if num_of_1:
            count *= permutation(room, num_of_1)
        
        return count
        
    if __name__ == "__main__":
        validate()
        # case-insensitive
        unique_num = count_unique_anagram(sys.argv[1].lower())
        sys.stdout.write(f"{unique_num}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
  • 相关阅读:
    wincc定时器功能介绍
    一键批量剪辑:视频随机分割新玩法,高效剪辑不再难
    git&&gitHub
    学生为什么要在CSDN写博客?
    2022年全球6家最具技术实力的的智能合约审计公司盘点
    中英翻译《”绿色“一词及其不同含义》
    Anaconda的安装使用及pycharm设置conda虚拟环境
    KOOM原理讲解(上)-JAVA内存分析
    供应叶酸PEG试剂Folic acid-PEG-Azide,FA-PEG-N3,叶酸-聚乙二醇-叠氮
    史上最强HashMap源码深度解析(3w字图文并茂)
  • 原文地址:https://blog.csdn.net/weixin_42815846/article/details/126654370