美文网首页软件测试自动化测试
Selenium WebDriver——如何测试REST API

Selenium WebDriver——如何测试REST API

作者: 软测小生 | 来源:发表于2019-09-25 16:48 被阅读0次

    文章首发于微信公众号软测小生
    前言:
    关于如何使用selenium webdriver测试REST api的问题,你可以在StackOverflow.com上看到很多相关的问题。不熟悉自动化测试的新人有时不理解Selenium仅仅基于WebUI做自动化测试。但是,如果你想使用Selenium为UI测试执行一些数据设置/数据清理,那么可以通过一些额外的库来实现这一点;这就是我们将在本文中看到内容。

    如果你只需要测试api,那么建议浏览这篇文章:Jmeter如何测试REST API /微服务

    Web UI测试存在的问题:

    • 慢(这是因为你的浏览器首先向服务器发送一个请求以获取某些信息,一旦获得所需数据,可能需要一些时间来处理数据,并通过下载的图片和应用样式使其显示在表格中/或者以适配的格式显示,所以你必须等待整个过程完成之后才能与应用程序进行交互);
    • 费时;
    • 对于测试不同的浏览器,可能必须重复相同的测试集;
    • 浏览器是独立于selenium脚本的进程。所以同步总是一个问题;
    • UI测试有很多依赖项,比如Browsers/Versions/Grid/Driver等等。

    因此,这并不意味着我们应该总是做API级别的测试并发布产品; 我们应该尝试尽可能的进行API级别测试。 我们可以只为UI测试提供较小覆盖率。

    REST API测试:
    与Selenium WebDriver UI测试相比,REST API测试并不难,大多数api都是GET / POST / PUT / PATCH / DELETE请求之一:

    • GET 用于从后端获取信息以显示在UI中;
    • POST 用于在后端添加新信息;
    • PUT用于更新/替换任何现有信息;
      • PATCH 用于部分更新;
    • DELETE 用于从后端删除信息。

    如果你的目的是对REST api进行详尽的测试,我建议看看JMeter。你可以查看下面关于使用JMeter进行REST API测试的文章。

    假设你使用testNG/Junit这样的框架,并使用Selenium进行应用程序UI测试 --而现在希望在相同的框架中也包含API测试 --可能需要快速设置数据或断言等,那么接下来就让我们看看如何在本文中完成。

    依赖包

    在maven文件中添加如下依赖:

    <dependency>
        <groupId>com.mashape.unirest</groupId>
        <artifactId>unirest-java</artifactId>
        <version>1.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.jtwig</groupId>
        <artifactId>jtwig-core</artifactId>
        <version>5.87.0.RELEASE</version>
    </dependency>
    
    • Unirest是一个简单的轻量级流畅式HTTP请求库
    • JTwig是一个非常简单的模板引擎

    程序示例:

    我将考虑这个应用程序进行测试。

    <font color=red size=3>【注意:本文并没有去下载该开源项目部署到本地,而是使用了已经部署在GitHub上的该项目作为学习使用,Live Demo: https://restool-sample-app.herokuapp.com/,倘若你有兴趣部署可以尝试下自己部署】</font>

    使用Rest API列出所有可用的联系人,添加/编辑/删除联系人;它还使用Angular构建了比较友好的UI界面;你可以克隆并部署到你的本地运行。
    一旦上述应用程序部署成功并启动,就可以使用API GET请求获取联系人列表,显示在UI界面上。

    1-获取联系人

    当您访问应用程序的主页时,它会列出所有可用的联系人。


    显示所有人

    如果监视Chrome-network中的Network,可以看到发送了一些API GET请求来获取联系人列表。

    • 如果你不确定在哪里检查,在Chrome页面按下F12,Chrome开发工具将会出现。
    • 检查API url的header部分
    F12 开发者工具
    本地部署的地址
    https://localhost:4200/api/contacts?q=
    而本文例子使用如下Live Demo链接:
    https://restool-sample-app.herokuapp.com/
    

    你可以看到以下格式的JSON Response:

    [
        {
            "id": "xiyydaS9CLqV",
            "thumbnail": "https://www.hbo.com……",
            "name": "Tyrion Lannister",
            "realName": "Peter Dinklage",
            "location": "Winterfell",
            "isAlive": true
        }
    ]
    

    你可以通过应用程序添加联系人,修改联系人,删除联系人

    2- GET Request:

    一旦应用程序启动,可以使用API GET请求获取联系人列表,以便在应用程序中显示数据。
    可以使用Unirest发出上面说到的GET请求,如下所示:

    String searchQueryApi = "https://restool-sample-app.herokuapp.com/api/character?search=";
    
    JsonNode body = Unirest.get(searchQueryApi)
                            .asJson()
                            .getBody();
    System.out.println(body);         // 打印完整的json响应信息
    System.out.println(body.getArray().length());  // 打印item编号
    

    其请求如下图所示:


    search Query Api

    也可以在测试框架中进行简单的断言。
    例如下面的示例代码确认API响应中的所有数据是否都显示在UI中:

    driver = new ChromeDriver();
    driver.manage().window().maximize();
    driver.get("https://restool-sample-app.herokuapp.com/");
    List<WebElement> contacts = driver.findElements(By.cssSelector("tbody > tr"));
    Assert.assertEquals(contacts.size(), body.getArray().length(), "The contacts not equals with Body length");
    

    3- POST Request:

    每当我们试图添加新的联系人时,就会发送POST请求并携带如下格式JSON作为Body:

    {
        "thumbnail": "https://www.hbo.com……",
        "name": "Test Name",
        "realName": "Test Real Name",
        "location": "Test location",
        "isAlive": false
    }
    

    如果你的目标是自己发送请求,那么您可能不希望在JSON文件中硬编码任何值。这就是我们使用JTwig模板引擎的地方。
    首先,我在模板下面创建。

    {
      "thumbnail": "{{URL}}",
      "name": "{{name}}",
      "realName": "{{realName}}",
      "location": "{{location}}",
      "isAlive": true
    }
    

    我将上面的JSON保存在一个名为“contact.json”的文件中。现在我可以读取模板并在运行时替换这些值,如下所示:

    JtwigTemplate template = JtwigTemplate.classpathTemplate("contact.json");
    JtwigModel model = JtwigModel.newModel()
        .with("URL", "http://n.sinaimg.cn/ent/transform/20160502/n4qY-fxrunru8640821.png")
        .with("name", "TestName")
        .with("realName", "TestRailName")
        .with("location", "Winterfell");
    template.render(model);//用上述的值替换模板表达式中的值
    

    接下来可以发送POST请求创建新的联系人了(发送POST请求之后,在这里还可以通过UI界面进行检查联系人是否成功显示在UI界面,此处不做详细Demo)

    String postApi = "https://restool-sample-app.herokuapp.com/api/character";
    Unirest.post(postApi)
        .header("accept", "application/json")
        .header("Content-Type", "application/json")
        .body(template.render(model))
        .asJson();
    
    成功添加雪诺

    使用上面这个方法,我们可以在应用程序中快速的添加联系人。

    假设页面最多只能显示50个联系人,你需要点击翻页按钮查看更多联系人,但是在本地/QA环境中,当你启动一个新的应用程序时,可能没有足够的联系人来测试该显示功能;
    如果页面对象公开了一个方法来添加联系人,则需要调用50多次,通过UI界面添加联系人可能非常耗时,由于同步问题,它可能随时会失败,并且还需要处理:比如当用例重试失败或者退出导致测试失败等情况。

    但是使用Api,您可以轻松地修改页面对象,如下所示,现在你可以用它来建立数据等等。它应该比UI方法快得多,而且更不容易出错。

    class ContactsPage{
        //all find bys
        //methods for interacting with web elements
        public void addContacts(int numberOfContacts){
            String postApi = "https://restool-sample-app.herokuapp.com/api/character";
            for(int i = 0; i<numberOfContacts; i++){
                Unirest.post(postApi)
                        .header("accept", "application/json")
                        .header("Content-Type", "application/json")
                        .body(template.render(model)) 
                        .asJson();
            }
    
        }
    
    }
    

    Unirest可以很容易地在page对象中使用,如上面的示例所示。

    4- 编辑请求

    要编辑联系人,我们需要发送如下所示的PUT请求。

    String editApi = "https://restool-sample-app.herokuapp.com/api/character/{contact_id}";
    
    JtwigModel model = JtwigModel.newModel()
            .with("name", "Snow")
            .with("location", "Winterfell");
    
    Unirest.put(editApi)
            .routeParam("contact_id", "T2S6kHv4cS1A")
            .header("accept", "application/json")
            .header("Content-Type", "application/json")
            .body(template.render(model))
            .asJson();
    

    更新了Name:


    已经更新为Snow

    5- 删除请求

    删除就这就更简单了。

    String editApi = "https://restool-sample-app.herokuapp.com/api/character/{contact_id}";
    
    Unirest.delete(editApi)
            .routeParam("contact_id", "T2S6kHv4cS1A")
            .asJson();
    

    我们可以使用这个API来清理测试是新建的数据,这样就保持测试之后的数据清洁,不会过多的新建无用甚至垃圾数据。

    public class ContactsPageTest{  
        private String editApi = "https://localhost:4200/api/contacts/{contact_id}";
    
        @Test
        public void someUItest1(){
            //
        }
    
        @Test
        public void someUItest2(){
            //
        }
    
        @AfterTest
        public void teardown(){
            for(String contactid: listOfContacts){
                Unirest.delete(editApi)
                .routeParam("contact_id", contactid)
                .asJson();
            }
        }
    
    }
    

    总结:
    通过在现有的测试框架/页面对象中使用Unirest,可以和REST api进行交互,还可以使用这些api在应用程序中进行快速设置数据,以便进行快速功能验证;
    正如上面的示例中所提到的,只要可能,就尽量使用api进行测试。

    完整Demo代码(请配合上述介绍使用):

    package com.morningstar.automation.pdf.Download;
    
    import org.jtwig.JtwigModel;
    import org.jtwig.JtwigTemplate;
    import org.openqa.selenium.WebDriver;
    import org.testng.annotations.AfterClass;
    import org.testng.annotations.Test;
    import com.mashape.unirest.http.JsonNode;
    import com.mashape.unirest.http.Unirest;
    
    public class testRestAPI {
        WebDriver driver;
    
        @Test
        public void testOne() throws Exception {
            //GET Request:
            String searchQueryApi = "https://restool-sample-app.herokuapp.com/api/character?search=";
            JsonNode body = Unirest.get(searchQueryApi).asJson().getBody();
            System.out.println(body); // 打印完整的json响应信息
            System.out.println(body.getArray().length()); // 打印item编号
            
            //验证联系人数量与UI界面显示的数量
    //      driver = new ChromeDriver();
    //      driver.manage().window().maximize();
    //      driver.get("https://restool-sample-app.herokuapp.com/");
    //      List<WebElement> contacts = driver.findElements(By.cssSelector("tbody > tr"));
    //      Assert.assertEquals(contacts.size(), body.getArray().length(), "The contacts not equals with Body length");
            
            //添加一个新的联系人
            JtwigTemplate template = JtwigTemplate.classpathTemplate("contact.json");
    //      JtwigModel model = JtwigModel.newModel()
    //              .with("URL", "http://n.sinaimg.cn/ent/transform/20160502/n4qY-fxrunru8640821.png")
    //              .with("name", "TestName")
    //              .with("realName", "TestRailName")
    //              .with("location", "Winterfell");
    //      template.render(model); //gives the json in the above format by replacing the template expressions
            
            //GET Request:
    //      String postApi = "https://restool-sample-app.herokuapp.com/api/character";
    //      Unirest.post(postApi)
    //              .header("accept", "application/json")
    //              .header("Content-Type", "application/json")
    //              .body(template.render(model))
    //              .asJson();
            
            //编辑请求, PUT
            String editApi = "https://restool-sample-app.herokuapp.com/api/character/{contact_id}";
            JtwigModel model = JtwigModel.newModel()
                    .with("name", "Snow")
                    .with("location", "Winterfell");
            Unirest.put(editApi)
                    .routeParam("contact_id", "T2S6kHv4cS1A")
                    .header("accept", "application/json")
                    .header("Content-Type", "application/json")
                    .body(template.render(model))
                    .asJson();
            
            //删除请求 Delete
    //      String editApi = "https://restool-sample-app.herokuapp.com/api/character/{contact_id}";
    //      Unirest.delete(editApi)
    //              .routeParam("contact_id", "T2S6kHv4cS1A")
    //              .asJson();
        }
    
        @AfterClass
        public void tearDown() {
            // driver.quit();
        }
    }
    

    contact.json文件就放置于根目录下斜体样式:

    {
      "thumbnail": "http://n.sinaimg.cn/ent/transform/20160502/n4qY-fxrunru8640821.png",
      "name": "{{name}}",
      "realName": "{{realName}}",
      "location": "{{location}}",
      "isAlive": true
    }
    

    相关文章

      网友评论

        本文标题:Selenium WebDriver——如何测试REST API

        本文链接:https://www.haomeiwen.com/subject/qzhwuctx.html