• 自动测试LeetCode用例方法


    自动合并测试LeetCode解题方法

    在leetcode.com上答题,Run Code或者Sumbmit通常要Spending一会,如果提交一次就Accepted那还好,如果反复Wrong Answer,很耽误时间。为了调高效率和减少挫折(来回提交,一直Wrong Answer倍受打击),我采取在本地Jupyter notebook上coding,测试通过后再提交的方式。本篇主要介绍自动测试的方法。

    1. LeetCode模式

    打开leetcode一道题后,左侧有一个Description选项卡,该选项卡面板内有题目、命题描述、还列举了多个Example,每个Example都有一个Input和一个Output,有的还会有一个Explanation;右侧是coding区,coding区自动生成 class Solution模板。

    class Solution: def method(self,**kwargs):

    例如,第1071号题Decription区的Example内容如下

    1071号题coding区初始化模板如下

    2. Jupyter notebook编写代码

    将LeetCode中coding模板内容复制到jupyter notebook中,编写解题代码。假如1071号题的代码如下。

    class Solution:
        def gcdOfStrings(self, str1: str, str2: str) -> str:
            if len(str1) < len(str2):
                tmp, str2, str1 = str2, str1, tmp
            len_str2, len_str1 = len(str2), len(str1)
            
            for i in range(1,len_str2+1):
                d2,m2 = divmod(len(str2),i)
                if m2==0 and str2==str2[:d2]*i:
                    d1, m1 = divmod(len(str1),d2)
                    if m1==0 and str1==str2[:d2]*d1:
                        return str2[:d2]
            return ""
    

    3. 手动逐一测试

    手动逐一测试,就是编写好method内容后,实例化Solution,然后复制Decription中每个Example中的Input,传入实例的方法,执行后观察结果是否与Example中Output一致。

    # 实列化类
    s = Solution()
    
    # 测试Example中的用例1
    # Input: str1 = "ABCABC", str2 = "ABC"
    # Output: "ABC"
    
    str1 = "ABCABC"
    str2 = "ABC"
    s.gcdOfStrings(str1,str2)
    
    
    'ABC'
    
    # 测试Example中的用例2
    # Input: str1 = "ABABAB", str2 = "ABAB"
    # Output: "AB"
    
    str1 = "ABABAB"
    str2 = "ABAB"
    s.gcdOfStrings(str1,str2)
    
    'AB'
    
    # 测试Example中的用例3
    # Input: str1 = "LEET", str2 = "CODE"
    # Output: ""
    
    str1 = "LEET"
    str2 = "CODE"
    s.gcdOfStrings(str1,str2)
    
    ''
    
    # 测试Example中的用例4
    # Input: str1 = "ABCDEF", str2 = "ABC"
    # Output: ""
    
    str1 = "ABCDEF"
    str2 = "ABC"
    s.gcdOfStrings(str1,str2)
    
    ''
    

    4. 自动合并测试

    4.1 复制测试用例

    从LeetCode问题页面复制所有Example内容,赋值给str_Examples。这里用3对双引号,内容可以原封不动。Example描述有规律,而且每个题的Example描述都是同样的模式,所以我们可以定义一个通用方法来获取测试用例。

    str_Examples ="""Example 1:
    
    Input: str1 = "ABCABC", str2 = "ABC"
    Output: "ABC"
    Example 2:
    
    Input: str1 = "ABABAB", str2 = "ABAB"
    Output: "AB"
    Example 3:
    
    Input: str1 = "LEET", str2 = "CODE"
    Output: ""
    Example 4:
    
    Input: str1 = "ABCDEF", str2 = "ABC"
    Output: ""
    """
    

    4.2 定义获取用例方法

    • 目标是把Input、Output提取出来,并成对组织好。
    • 通过观察,可采用正则化split、字符串split、replace、strip等方法提取Input、Output内容
    • 输入参数和输出存入list,每个用例可能有多个参数,所以用例参数用dict装载,方便调用测试方法时用**kwargs传入。

    注意:因为输入用例文档用的""""",引号里内容还含有双引号",split后会把里面的引号当做字符内容,导致结果出乎意料。

    def get_test_cases(str_examples):
        import re
        params =re.split('Example.+?:',str_examples.replace('
    ','').replace(' ',''))[1:]
        cases_arg, cases_ans = [], []
        for param in params:
            kawargs = {}
            args_,ans = param.split('Output:')
            args_ = args_.replace('Input:','').strip().split(',')
            cases_ans.append(ans)
            for arg in args_:
                key,val = arg.split("=")
                kawargs[key] = val
            cases_arg.append(kawargs)
        return zip(cases_arg, cases_ans)
    

    4.3 定义自动测试方法

    • leetcode每个题生成的默认代码类名都是Solution,但是函数名根据题目来取,因题而异。dir(Solution())能够获取Solution的方法名列表,前面部分是内建方法名,最后一个是自定义的解题方法(这里考虑只有一个自建函数)。用dir(Solution())[-1]获取函数名,然后使用getattr获取实例方法f。
    • 传入参数为get_usecases返回的结果——zip打包的测试用例输入和输出,for迭代或取每一个测试用例的输入参数kwargs(Example中的Input)和输入answer(Example中的Output)。
    • 迭代测试用例,断言 f(**kwargs) == answer 。如果每个断言成功,返回'Accepted',否则try...except捕获断言失败,返回'Wrong Answer'。
    def auto_test(test_cases):
        f = getattr(Solution(), dir(Solution())[-1])
        try:
            for kwargs, answer in test_cases:
                print(kawargs,answer)
                assert f(**kwargs) == answer        
            return 'Accepted'
        except Exception as e:  
            print(e)
            return 'Wrong Answer'
    

    4.4 what?! 神奇问题bug ?

    • 先调用get_test_cases获取测试用例,然后将结果传入auto_test进行测试,结果是'Wrong Answer';
    • 直接调用auto_test(get_test_cases()),结果也是'Wrong Answer'。
    • 然而调用get_test_cases获取测试用例test_cases后,先迭代一遍test_cases,再将test_cases传入auto_test,结果为'Accepted'。
    test_cases = get_test_cases(str_Examples)
    auto_test(test_cases)
    
    name 'kawargs' is not defined
    
    'Wrong Answer'
    
    auto_test(get_test_cases(str_Examples))
    
    name 'kawargs' is not defined
    
    'Wrong Answer'
    
    for input_args,out in test_cases:
        print(input_args, out)
    auto_test(test_cases)    
    
    {'str1': '"ABABAB"', 'str2': '"ABAB"'} "AB"
    {'str1': '"LEET"', 'str2': '"CODE"'} ""
    {'str1': '"ABCDEF"', 'str2': '"ABC"'} ""
    
    
    'Accepted'
    

    4.5 编程陷阱

    陷阱1:"""doc""",三对双引号中内容带引号,没去除引号被当做字符内容,导致Solution算法“失灵”。

    doc = """ABC "efg" HIGK"""  # efg带引号
    doc.split()                 # 分割后'"efg"'而不是'efg',不注意还看不出来
    
    ['ABC', '"efg"', 'HIGK']
    

    避坑办法:.replace('"','')去除内容引号

    def get_test_cases(str_examples):
        import re
        params =re.split('Example.+?:',str_examples.replace('
    ','').replace(' ',''))[1:]
        cases_arg, cases_ans = [], []
        for param in params:
            kawargs = {}
            args_,ans = param.split('Output:')
            args_ = args_.replace('Input:','').strip().split(',')
            # ans.replace('"','')
            cases_ans.append(ans.replace('"','')) 
            for arg in args_:
                key,val = arg.split("=")
                 # val.replace('"','')
                kawargs[key] = val.replace('"','')
            cases_arg.append(kawargs)
        return zip(cases_arg, cases_ans)
    

    陷阱2:try...except不仅捕获assert断言成功失败,在try中print(kawargs,answer),‘kawargs’写错也导致进入except块。

    避坑办法:try...except Exception as e打印异常,debug错误

    # 捕获except异常并不等于assert断言失败
    try:
        print(what)
        assert 1==1
    except Exception as e:
        print(e)
    
    name 'what' is not defined
    
    def auto_test(test_cases):
        f = getattr(Solution(), dir(Solution())[-1])
        try:
            for kwargs, answer in test_cases:
                print(kwargs,answer)
                assert f(**kwargs) == answer        
            return 'Accepted'
        except Exception as e:  
            print(e)
            return 'Wrong Answer'
    

    陷阱2:zip只能迭代一次,looping后被释放,外部迭代一次后再传入auto_test,不进入for迭代,造成外部迭代后返回正确'Accepted'的假象。

    val =[1,2,3]
    key = ['a','b','c']
    ziped = zip(key,val)
    print('----first iter zip---------')
    for (key,val) in ziped:    
        print(key,val)
        
    print('----second iter zip---------')
    for (key,val) in ziped:
        
        print(key,val)
    
    ----first iter zip---------
    a 1
    b 2
    c 3
    ----second iter zip---------
    

    避坑办法:头一次掉这坑,刷新zip认知,记住它。

    4.6 再次测试

    test_cases = get_test_cases(str_Examples)
    auto_test(test_cases)
    
    {'str1': 'ABCABC', 'str2': 'ABC'} ABC
    {'str1': 'ABABAB', 'str2': 'ABAB'} AB
    {'str1': 'LEET', 'str2': 'CODE'} 
    {'str1': 'ABCDEF', 'str2': 'ABC'} 
    
    'Accepted'
    
    auto_test(get_test_cases(str_Examples))
    
    {'str1': 'ABCABC', 'str2': 'ABC'} ABC
    {'str1': 'ABABAB', 'str2': 'ABAB'} AB
    {'str1': 'LEET', 'str2': 'CODE'} 
    {'str1': 'ABCDEF', 'str2': 'ABC'} 
    
    'Accepted'
    

    5. 总结

    step1: 在Jupyter notebook中编写LeetCode问题解答代码
    class Solution: def method(self,**kwargs):
    step2: 复制LeetCode中Example内容复制给变量str_Examples
    step3: 使用get_test_cases获取测试用例
    step4: 调用auto_test进行自动测试,返回'Accepted'所有测试用例通过,'Wrong Answer'表示存在没能通过的用例。

    LeetCode问题Description中所有Example测试通过,即Run Code结果为'Accepted',并不代表Submit后的结果一定会为'Accepted'。相反,Run Code结果为'Wrong Answer',那么Submit后结果一定是'Wrong Answer'。因为Description中的Example只是部分示例,是Submit后验证的测试用例集合的子集。

    def get_test_cases(str_examples):
        import re
        params =re.split('Example.+?:',str_examples.replace('
    ','').replace(' ',''))[1:]
        cases_arg, cases_ans = [], []
        for param in params:
            kawargs = {}
            args_,ans = param.split('Output:')
            args_ = args_.replace('Input:','').strip().split(',')
            # ans.replace('"','')
            cases_ans.append(ans.replace('"','')) 
            for arg in args_:
                key,val = arg.split("=")
                 # val.replace('"','')
                kawargs[key] = val.replace('"','')
            cases_arg.append(kawargs)
        return zip(cases_arg, cases_ans)
    
    def auto_test(test_cases):
        f = getattr(Solution(), dir(Solution())[-1])
        try:
            for kwargs, answer in test_cases:
                print(kwargs,answer)
                assert f(**kwargs) == answer        
            return 'Accepted'
        except Exception as e:  
            print(e)
            return 'Wrong Answer'
    
  • 相关阅读:
    Servlet开发
    HTML实现页面自动跳转的五种方法
    AVAYA话机管理
    AVAYA路由
    报关相关知识
    基本杆法
    AVAYA初始配置
    加塞和瞄准
    基本杆法图解
    AVAYA拨号计划
  • 原文地址:https://www.cnblogs.com/gradual/p/14111829.html
Copyright © 2020-2023  润新知