Day924.自动化测试 -系统重构实战

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day924.自动化测试 -系统重构实战相关的知识,希望对你有一定的参考价值。

自动化测试

Hi,我是阿昌,今天学习记录的是关于自动化测试的内容。

自动化测试是一个很容易产生“争议”的话题,也经常会有一些很有意思的问题。

  • 自动化测试不是应该由测试同学来编写吗,开发是不是没有必要学吧?
  • 之前一个自动化测试都没写过,怎么开始落地呢?
  • 编写自动化测试代码意味着要写更多的代码,这能带来什么好处呢?

在这个过程中的代码”


一、示例介绍

这个示例是一个登录的场景。

当用户在登录页面输入正确的账户和密码时,能正常跳转到登录界面,否则提示登录失败的信息。

下面是关键的代码。

  • 登录页面代码
public class LoginActivity extends AppCompatActivity 
    private LoginLogic loginLogic = new LoginLogic();

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        final EditText usernameEditText = findViewById(R.id.username);
        final EditText passwordEditText = findViewById(R.id.password);
        final Button loginButton = findViewById(R.id.login);
        loginButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                boolean success = loginLogic.login(LoginActivity.this,usernameEditText.getText().toString(),
                        passwordEditText.getText().toString());
                if (success) 
                    //登录成功跳转主界面
                    startActivity(new Intent(LoginActivity.this, MainActivity.class));
                 else 
                    //登录失败进行提示
                    Toast.makeText(LoginActivity.this, "login failed", Toast.LENGTH_LONG).show();
                
            
        );
    

  • 登录逻辑代码
public class LoginLogic 
    public boolean login(Context context,String username, String password) 
        if (!isUserNameValid(username) || !isPasswordValid(password)) 
            return false;
         else 
            //通过服务器判断账户及密码的有效性
            boolean result = checkFromServer(username, password);
            if (result) 
                //登录成功保持本地的信息
                SharedPreferencesUtils.put(context, username, password);
            
            return result;
        
    
    // 为了进行演示,去除通过服务器鉴定的逻辑,当用户输入特定账号及密码为时则验证成功
    private static boolean checkFromServer(String username, String password) 
        if (username.equals("123@163.com") && password.equals("123456")) 
            return true;
        
        return false;
    
    private boolean isUserNameValid(String username) 
        if (username == null) 
            return false;
        
        if (username.contains("@")) 
            return Patterns.EMAIL_ADDRESS.matcher(username).matches();
         else 
            return !username.trim().isEmpty();
        
    
    private boolean isPasswordValid(String password) 
        return password != null && password.trim().length() > 5;
    

注意,这里为了简化演示,将验证的逻辑写死在本地了。另外,账户密码有两个核心的验证规则。

  • 账户不能为空,需要符合邮箱规则。
  • 密码不能为空,长度需要超过 5 个字符。

二、搭建测试环境

当通过默认的编辑器创建新的项目工程时,编辑器会自动创建好测试的运行配置,一般无需修改。

如果要增加测试框架,就把测试框架的 Maven 坐标添加到对应的 dependencies 中即可。

Gradle 中的测试相关配置代码是后面这样。

android
	  defaultConfig 
	    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
	
	dependencies 
	    testImplementation 'junit:junit:4.13.2'
	    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
	    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
	

接下来,在默认的 /src/test 或 /src/androidTest 目录下编写用例。

注意,test 目录的用例运行不依赖于设备,androidTest 目录下的用例运行需要依赖设备。

一般来说,自动化测试分为小型、中型和大型三种,逐个看看这三种测试怎么落地。


三、小型自动化测试实践

小型测试是指单元测试,用于验证应用的行为,一次验证一个类。

在这个示例中,LoginLogic 主要承担的是登录逻辑,这里就以账户密码的验证逻辑为例,演示一下小型测试的编写,这两个逻辑的主要规则是这样。

  • 账户不能为空,需要符合邮箱规则。
  • 密码不能为空,长度需要超过 5 个字符。

接着,设计对应的测试用例。

注意,用例的设计应该包含正常和异常的验证场景,具体的测试场景是后面这样。

  • 输入大于 6 个字符长度的密码,验证成功。
  • 输入为 Null 的字符,验证失败。
  • 输入小于 5 个字符长度的密码,验证失败。
  • 输入等于 5 个字符长度的密码,验证失败。

下面新建一个 LoginLogicTest 的测试类,按照上述的测试场景编写用例。

这里我会采用 given(输入)、when(执行)、then(结果)的形式,让用例更加结构化,便于理解和维护。

具体的测试用例代码如下:


public class LoginLogicTest 
    @Test
    public void should_return_false_when_password_is_null() 
        LoginLogic loginLogic = new LoginLogic();
        String password = null;
        boolean result = loginLogic.isPasswordValid(password);
        Assert.assertFalse(result);
    
    @Test
    public void should_return_false_when_password_length_is_less_than_5() 
        LoginLogic loginLogic = new LoginLogic();
        String password = "1234";
        boolean result = loginLogic.isPasswordValid(password);
        Assert.assertFalse(result);
    
    @Test
    public void should_return_false_when_password_length_is_equal_5() 
        LoginLogic loginLogic = new LoginLogic();
        String password = "12345";
        boolean result = loginLogic.isPasswordValid(password);
        Assert.assertFalse(result);
    
    @Test
    public void should_return_true_when_password_length_greater_than_5() 
        LoginLogic loginLogic = new LoginLogic();
        String password = "123456";
        boolean result = loginLogic.isPasswordValid(password);
        Assert.assertTrue(result);
    

通过点击用例旁的运行箭头可以执行用例,如下图所示。

运行完就可以直接查看运行结果了,如下图所示。

可以看出小型测试的执行时间还是比较快的,4 个用例总共用了 7 ms。

此外,还可以用另一种方式执行测试用例:使用命令行./gradlew test,运行 test 目录下的测试用例,如下图所示。

执行完测试后,在 /build/reports/tests 下可以查看到对应的测试报告,报告截图如下所示,从中能得到每个用例具体的执行情况和执行时间。


四、中型自动化测试实践

中型测试是指集成测试,用于验证模块内堆栈级别之间的互动或相关模块之间的互动。

常用的测试框架有两种:Robolectric 和 Espresso。

在登录示例中,当 LoginLogic 的 login 方法被调用时,程序主逻辑首先会执行对账户名和密码的校验,接着通过服务器对账户密码的有效性做校验。

当登录成功时,程序主逻辑会通过 SharedPreferences 保存用户的信息,并在最后返回登录的状态。

从示例代码中可以看出,LoginActivity 类主要都是 UI 的操作,所以对该类主要覆盖的是 UI 相关的测试;

对于 LoginLogic 的类核心方法,login 主要负责整体的业务验证逻辑。

如何通过 Espresso 和 Robolectric 对这两个类进行中型自动化测试的覆盖。

1、Espresso 的使用

Espresso 是 Google 官方提供的界面测试框架,使用简洁且可靠。

它可以声明预期、交互和断言,不用直接访问底层应用的 Activity 和视图,可以防止测试不稳定,提高测试运行的速度。

根据用户 UI 上的主要操作,将覆盖以下两个主要的业务场景。

  • 用户输入正确的用户名(123@163.com)和密码(123456),点击登录按钮能成功跳转到登录界面。
  • 用户输入错误的用户名(123)和密码(456),点击登录按钮提示登录失败的 Toast。

根据测试场景,使用 Espresso 对 LoginActivity 设计的测试用例代码如下。

 public class LoginActivityTest 
    @Test
    public void should_start_main_activity_when_execute_login_given_valid_username_and_password() 
        ActivityScenario.launch(LoginActivity.class);
        onView(withId(R.id.username)).perform(typeText("123@163.com"));
        onView(withId(R.id.password)).perform(typeText("123456"));
        Intents.init();
        onView(withId(R.id.login)).perform(click());
        intended(allOf(
                toPackage("com.jkb.junbin.autotestdemo"),
                hasComponent(hasClassName(MainActivity.class.getName()))));
    
    @Test
    public void should_show_failed_toast_when_execute_login_given_invalid_username_and_password() 
        ActivityScenario<LoginActivity> launch = ActivityScenario.launch(LoginActivity.class);
        onView(withId(R.id.username)).perform(typeText("123"));
        onView(withId(R.id.password)).perform(typeText("456"));
        onView(withId(R.id.login)).perform(click());
        View decorView = null;
        launch.onActivity(activity -> 
            activity.getWindow().getDecorView();
        );
        onView(withText("login failed")).inRoot(withDecorView(not(decorView))).check(matches(isDisplayed()));
    

Espresso 提供的 API 能方便地进行元素的定位、执行操作和断言。

在上述两个用例中,用 onView 定位元素,用 perform 执行操作,用 check 进行断言。

如果想了解 Espresso 更多的操作 API,可以参考官网的介绍

执行完上述用例后,运行结果是下图这样:

可以看到,这两个测试用例在模拟器中整体的运行时间在 5s 左右。

相比小型测试,中型测试耗时会更长。

并且用例运行需要依赖设备,这让运行测试的成本更高。

用例的执行过程如下图所示:


2、Robolectric 的使用

Robolectric 框架能为 Android 带来快速可靠的测试。

具体来说,依赖该框架的测试用例无须在真机或者模拟器上运行,在本地工作站上的 JVM 内完成运行即可,一般只需要几秒。

下面以 LoginLogic 的 login 方法为例,介绍一下 Robolectric 的使用。

这里需要覆盖下面三个主要的业务场景。

  • 传入空的字符串或者密码,返回失败。
  • 传入错误的账户及密码,返回失败。
  • 传入正确的账户及密码,返回成功,并且进行数据缓存。

后面是测试用例的代码。

@RunWith(RobolectricTestRunner.class)
public class LoginLoginMediumTest 
    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
    @Test
    public void should_return_false_when_given_invalid_username_or_password() 
        LoginLogic loginLogic = new LoginLogic();
        boolean nullUserNameResult = loginLogic.login(mContext, null, "123");
        Assert.assertFalse(nullUserNameResult);
        boolean nullPasswordResult = loginLogic.login(mContext, "123", null);
        Assert.assertFalse(nullPasswordResult);
    
    @Test
    public void should_return_false_when_given_error_username_and_password() 
        //验证错误的账户及密码
        boolean result = new LoginLogic().login(mContext, "123", "456");
        Assert.assertFalse(result);
    
    @Test
    public void should_return_true_when_given_correct_username_and_password() 
        String username = "123@163.com";
        String password = "123456";
        //验证正确的账户及密码
        boolean result = new LoginLogic().login(mContext, username, password);
        Assert.assertTrue(result);
        //验证存在缓存信息
        String cachePassword = (String) SharedPreferencesUtils.get(mContext, username, "");
        Assert.assertEquals(password, cachePassword);
    

关于 Robolectric 的 API,可以参考官网的介绍

执行上述测试用例后,运行结果如下图。

从结果可以看出,用 Robolectric 框架进行测试,用例的执行时间在毫秒到秒之间。其中第二个用例的执行耗时超过 2s,是因为启动 Robolectric 框架需要一定的时间。同时我们也能体会到 Robolectric 的核心优势:无须依赖设备,可以快速在本地的 JVM 进行验证,得到快速的反馈。


五、大型自动化测试实践

大型测试是指端到端测试,用于验证跨越了应用的多个模块的用户操作流程。

前面介绍的 Espresso 和 Robolectric 主要是针对单个页面的测试场景,在实际的应用业务场景中,还有涉及跨应用和系统 UI 交互的场景。

通常会用 UI Automator 完成大型测试。

UI Automator 是一个界面测试框架,适用于整个系统和多个已安装应用间的跨应用功能界面测试。

它提供了一组 API,用于构建在用户应用和系统应用上执行交互的界面测试。

通过 UI Automator API,可以在测试设备中执行打开“设置”菜单或应用启动器等操作。

相比 Espresso 和 Robolectric 编写的白盒测试用例,UI Automator 测试框架非常适合编写黑盒式自动化测试,此类测试的测试代码不依赖于目标应用的内部实现细节。下面继续对登录示例进行完整的功能测试。

完整的用户测试场景是这样的:

用户在手机的任意界面,返回桌面启动测试的应用登录界面,然后输入正确的用户名和密码,成功跳转到主界面,验证主界面上显示的用户信息是否正确。这个用户场景会涉及到多个应用,其中包括了桌面和目标测试应用。

另外,应用内还会涉及到多个页面,主要是登录界面和主界面。

现在使用 UI Automator 框架进行测试,用例代码是这样。


@RunWith(AndroidJUnit4.class)
public class SmellTest 
    private static final String BASIC_SAMPLE_PACKAGE
            = "com.example.sample";
    private static final int LAUNCH_TIMEOUT = 5000;
    private UiDevice mDevice;
    @Before
    public void startActivityFromHomeScreen() 
        // 初始化UiDevice
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
        // 回到主界面
        mDevice.pressHome();
        // 等待launcher
        final String launcherPackage = mDevice.getLauncherPackageName();
        assertThat(launcherPackage, notNullValue());
        mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
                LAUNCH_TIMEOUT);
        // 启动目标APP
        Context context = ApplicationProvider.getApplicationContext();
        final Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);
        // 等待应用启动
        mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
                LAUNCH_TIMEOUT);
    
    //账户密码登录成功后主界面显示用户名
    @Test
    public void should_show_username_in_main_activity_when_login_success() 
        //输入账户名
        mDevice.findObject(By.res(BASIC_SAMPLE_PACKAGE, "username"))
                .setText("123");
        //输入密码
        mDevice.findObject(By.res(BASIC_SAMPLE_PACKAGE, "password"))
                .setText("123");
        //点击登录
        mDevice.findObject(By.res(BASIC_SAMPLE_PACKAGE, "login"))
                .click();
        //验证主界面上显示用户名信息
        UiObject2 text = mDevice
                .wait(Until.findObject(By.res(BASIC_SAMPLE_PACKAGE, "text")),
                        500);
        assertEquals(text.getText(), "123");
    

UI Automator 测试框架提供了一个 UiDevice 类,用于在运行目标应用的设备上访问和执行操作。

通过调用 findObject 方法,可以定位到元素和执行操作。

关于 UI Automator 更多的 API 使用,你可以参考官网文档

执行上述测试用例后,运行结果如下图所示:

下面这个动图展示了用例的执行过程,可以对比一下,这个过程如果用手工执行需要多久。

通过执行结果可知,该用例的执行时间实际为 9s,比中小型测试的执行时间更长,并且需要依赖真机或模拟器。

不过,该用例基本都是模拟用户对界面的点击操作,更贴近实际用户的真实使用场景。


六、总结

  • 小型测试能够快速帮验证代码中的核心逻辑和算法,通常使用的是 Junit 或者 Robolectric 等测试框架;
  • 中型测试能够帮验证代码中的一些核心组件交互流程,通常会用 Espresso 或者 Robolectric 等框架来完成;
  • 大型测试则能帮验证端到端的用户使用场景,通常使用的是 UIAutomator 或者 Appium 等框架。

重新思考一下开头的三个问题:

  • 第一个是测试由谁来写的问题。中小型的测试大部分都是根据代码设计来编写的。编写者需要了解原来代码的设计,精确到各个方法以及方法内部的条件分支和异常处理。所以,中小型测试应该由开发人员来编写。另外,也鼓励开发人员参与到大型端到端自动化测试的编写中。因为只有开发代码和测试代码一起共同维护,成本才是最低的。
  • 第二个问题是:之前一个自动化测试都没写过,怎么开始落地?对于开发人员来说,编写自动化测试用例的难度其实比功能开发设计还简单。
  • 第三个问题是关于自动化测试价值的问题。这里有一个前提:不认为开发完代码就意味着结束,结束应该是在有足够的质量保证的前提下。

所以,自动化测试是应用开发过

第一个自动化测试框架(day10)

一.自动化测试分类:

1.数据驱动:

  根据数据(读取excel)来去测试的。

2.代码驱动:

  测试用例都是写代码的。

3.关键字驱动:

  UI自动化,根据封装好的工具,输入关键字来测试,有点傻瓜式测试。

二.自动化框架:可以理解为工具的集合。日常工作中,根据工作需求,实现某些功能,封装起来。或结合其他自动化工具。

三.举例:数据驱动自动测试框架:

自动化测试框架步骤:

1.获取用例

2.调用接口

3.校验结果

4.发送测试报告

5.异常处理

6.日志模块

 码起来:【宗旨:一个函数只做一件事情】

提示:

1.用例文件的内容:

2.ATP文件的结构:

 一.在common.py里写OpCase类,及get_case,my_request,check_res,write_excel,dataToDict函数。

1.get_case()函数:获取用例。获取用例中需要的数据:url,请求方式,请求数据及预期结果,放到cases list里

import xlrd
from lib.log import atp_log

class OpCase(object):  
    def get_case(self,file_path):#获取用例的函数

        cases = [] #存放所有的case
        if file_path.endswith(\'.xls\') or file_path.endswith(\'.xlsx\'):  #判断用例文件是否是excel
            try:
                book = xlrd.open_workbook(file_path)
                sheet = book.sheet_by_index(0)
                for i in range(1,sheet.nrows):
                    row_data = sheet.row_values(i)  #获取每行
                    cases.append(row_data[4:8]) #将用例中第5列至第9列截取到cases list中
                atp_log.info(\'共读取%s条用例\'%(len(cases)))  #读取用例的条数
                self.file_path = file_path  #在实例中将路径(文件名)定义,在后面write_excel()函数中不需要再传入该参数,可以直接用,否则需要在write_excel()函数中传入参数file_path
            except Exception as e:
                atp_log.error(\'【%s】用例获取失败,错误信息:%s\'%(file_path,e))
        else:
            atp_log.error(\'用例文件不合法的,%s\'%file_path)
        return cases

 2.my_request()及dataToDict()函数,调用接口。通过url,data请求获取返回的内容,其中获取的data需要先转成字典

import requests

    def my_request(self,url,method,data):#参数url,method,data
        method = method.upper() #传入参数有可能会有大小写,统一转成大写字母
        data = self.dataToDict(data)  #将获取的数据做处理,转成字典
        try :
            if method==\'POST\': #判断method的类型
                res = requests.post(url,data).text  #发送请求并转换成字典
            elif method==\'GET\':
                res = requests.get(url,params=data).text
            else:
                atp_log.warning(\'该请求方式暂不支持。。\')  #调用日志文件
                res = \'该请求方式暂不支持。。\'
        except Exception as e:
            msg = \'【%s】接口调用失败,%s\'%(url,e)
            atp_log.error(msg)
            res = msg
        return res  #返回请求返回的内容


    def dataToDict(self,data):#从excel表里获取的data格式:username=fancy,passwd=abc123456,把数据转成字典

        res = {}
        data = data.split(\',\')
        for d in data:
            k,v = d.split(\'=\')
            res[k]=v
        return res

3.check_res()函数,校验结果成功或失败。将从服务器获取的返回结果与excel表里的预期结果比较,是否一致,若一致返回“成功”并写入结果,否则失败。

首先了解从my_request里获取的返回内容的样式:

excel表里预期结果的样式:

思路:将my_request()函数里返回的res样式变为:

然后判断预期结果check是否在res返回的这个字典里。

具体实现如下:

    def check_res(self,res,check):
        res = res.replace(\'": "\',\'=\').replace(\'": \',\'=\')  #两次替换,把res里返回的字典里所有的替换成=号(因为返回的Json串格式都是一样的)
        for c in check.split(\',\'): #将预期结果里的以逗号分开
            if c not in res: #判断预期结果里的list是否在返回结果res里
                atp_log.info(\'结果校验失败,预期结果:【%s】,实际结果【%s】\'%(c,res))
                return \'失败\'
        return \'成功\'

4.write_excel()函数,写excel。获取返回每条用例的执行结果cases_res。需导入xlutils模块。

注:在get_case()函数里,定义了: self.file_path = file_path  ,因此在该函数write_excel()中不需要再传入file_path参数,可以直接调用,否则需要在write_excel()函数中传入参数file_path。

 首先需要了解用例返回结果样式:返回一个二维数组:[[\'abcxx,‘通过’],[abxx2,‘失败’]....],将结果写入返回报文和测试结果列。

from xlutils import copy

        def write_excel(self,cases_res):
        # [ [\'dsfd\',"通过"] ,[\'sdfsdf\',\'失败\'] ]
        book = xlrd.open_workbook(self.file_path)
        new_book = copy.copy(book)
        sheet = new_book.get_sheet(0)
        row = 1  #从第二行开始写,第一行是表头
        for case_case in cases_res: #获取到每一个结果
            sheet.write(row,8,case_case[0]) #写第8列
            sheet.write(row,9,case_case[1]) #写第9列
            row+=1
        new_book.save(self.file_path.replace(\'xlsx\',\'xls\'))  #如果是xlsx结尾的将替换为xls结尾,保证excel可以打开

二.在lib下写发邮件的类:send_mail.py

发邮件是需要定义一些变量、常量,因此将这些内容写在配置文件setting里便于管理。

import yagmail
from conf import setting  #将setting文件里定义的常量导入
from lib.log import atp_log  #导入lib下的log文件,写日志
def sendmail(title,content,attrs=None):
    m = yagmail.SMTP(host=setting.MAIL_HOST,user=setting.MAIL_USER
                 ,password=setting.MAIL_PASSWRD,smtp_ssl=True  #当是qq邮箱时,需加入smtp_ssl=True
                 )
    m.send(to=setting.TO,subject=title,
           contents=content,
           attachments=attrs)
    atp_log.info(\'发送邮件完成\')

三.在conf下新建setting.py,配置文件里定义各种需要的变量,常量,修改时,方便。

import os
BASE_PATH = os.path.dirname(
    os.path.dirname(os.path.abspath(__file__))  #获取ATP所在的路径
)
MAIL_HOST=\'smtp.163.com\'
MAIL_USER=\'liu.****@163.com\'
MAIL_PASSWRD = \'p888****g421\'
TO = [
    \'5472*****@qq.com\',
]
LEVEL = \'debug\' #日志级别

LOG_PATH = os.path.join(BASE_PATH,\'logs\') #存放日志的路径,动态获取的,无论文件存放位置
CASE_PATH = os.path.join(BASE_PATH,\'cases\') #存放日志的路径
LOG_NAME=\'atp.log\' #日志的文件名

四.在lib下log.py文件。写日志文件,在其他文件抛异常时,会打印日志,直接调用该文件。

import logging,os
from logging import handlers
from conf import setting
class MyLogger():
    def __init__(self,file_name,level=\'info\',backCount=5,when=\'D\'):
        logger = logging.getLogger()  # 先实例化一个logger对象,先创建一个办公室
        logger.setLevel(self.get_level(level))  # 设置日志的级别的人
        cl = logging.StreamHandler()  # 负责往控制台输出的人
        bl = handlers.TimedRotatingFileHandler(filename=file_name, when=when, interval=1, backupCount=backCount, encoding=\'utf-8\')
        fmt = logging.Formatter(\'%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s\')
        cl.setFormatter(fmt)  # 设置控制台输出的日志格式
        bl.setFormatter(fmt)  # 设置文件里面写入的日志格式
        logger.addHandler(cl)
        logger.addHandler(bl)
        self.logger = logger

    def get_level(self,str):
        level = {
            \'debug\':logging.DEBUG,
            \'info\':logging.INFO,
            \'warn\':logging.WARNING,
            \'error\':logging.ERROR
        }
        str = str.lower()
        return level.get(str)


path = os.path.join(setting.LOG_PATH,setting.LOG_NAME) #拼好日志的绝对路径
atp_log = MyLogger(path,setting.LEVEL).logger  #日志级别
#直接在这里实例化,用的时候就不用再实例化了

五.在bin下新建start.py。start.py文件是启动文件,程序的入口,串联整个系统功能的所有逻辑。

import os,sys
BASE_PATH = os.path.dirname(
    os.path.dirname(os.path.abspath(__file__))  #获取ATP的路径
)
sys.path.insert(0,BASE_PATH)  #将ATP目录下的所有文件加到环境变量,这样保证以后在哪里运行都可以

from lib.common import OpCase #导入读取用例的类
from lib.send_mail import sendmail  #导入发邮件的类
from conf import setting  #导入配置文件
class CaseRun(object): #启动的类
    def find_cases(self):
        op = OpCase()  #实例化获取用例的类,便于调用OpCase()类下的函数
        for f in os.listdir(setting.CASE_PATH):#每次循环的时候读一个excel
            abs_path = os.path.join(setting.CASE_PATH,f)  #拼成绝对路径,f只是一个文件名
            case_list = op.get_case(abs_path)  #调用OpCase()类下的获取用例的函数,获取所有用例
            res_list = []
            pass_count,fail_count = 0,0
            for case in case_list:#循环每个excel里面所有用例
                url,method,req_data,check = case  #check:预期结果
                res = op.my_request(url,method,req_data) #调用完接口返回的结果,res:实际结果
                status = op.check_res(res,check)  #调用check_res()函数,校验预期结果&实际结果
                res_list.append([res,status])  #将返回结果和测试结果加入一个list,定义res_list,便于写入excel
                if status==\'成功\':
                    pass_count+=1
                else:
                    fail_count+=1
            op.write_excel(res_list)  #将res_list(返回报文,测试结果)写入excel
            msg = \'\'\'
            xx你好:
                本次共运行%s条用例,通过%s条,失败%s条。
            \'\'\'%(len(res_list),pass_count,fail_count)
            sendmail(\'测试用例运行结果\',content=msg,attrs=abs_path)  #发邮件

六.启动:在start.py文件中:

CaseRun().find_cases() #调用find_cases函数

 

欧了,整个简单的自动化框架包装完毕。

 

以上是关于Day924.自动化测试 -系统重构实战的主要内容,如果未能解决你的问题,请参考以下文章

Python实训day07am爬取数据接口webdriver自动化测试工具selenium

The tenth day of internship

Python自动化-Day1

924. 单词最短距离

Day536.Selenium自动化浏览器脚本爬虫 -python

924 a20170924 a20170920924 a2