REST・SOAP・(MQ) テストツール SoapUI を使ってみた

REST と SOAP のテストツールに SoapUI というものがあります.

REST のテストツールというと Postman/Newman が有名かと思いますが、SoapUI さんも中々に高機能でして、

  • Windows/Mac/Linux で実行可能
  • REST と SOAP の両方に対応している
  • HermsJMS をインストールすれば MQ も使える
  • API Driver/Stub の両方の役割をこなせる
  • テストケースを設定してアサーションできる
  • shell/bat で起動できる

このようにオールインワンで何でもこなせるテストツールになっています.
(REST/SOAP/MQ 全部こなせるのはデリバリー現場で非常に嬉しい)

ソースコードも公開されているので、何かあっても自分で調べられる安心感があります.
GitHub – SmartBear/soapui

Open Source版とPro版があります.
Pro版はテスト結果をExcelに出力するなど付加機能がたくさんありますが、Open Source版でも十分に使えるレベルです.

今回は SoapUI を REST で使ってみたいと思います.

サンプルプログラム

Spring Boot のサンプルを用意しました.

package com.projectrespite.soapuidemo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
@ApiModel(value = "Person", description = "This entity is person which have id, name and age fields.")
public class Person {

    @ApiModelProperty(value = "ID", example = "279818d8-89d0-43b2-9c62-a397b6a4ce50", required = true, position = 0)
    private String id;

    @ApiModelProperty(value = "Name", example = "箱崎太郎", required = true, position = 1)
    private String name;

    @ApiModelProperty(value = "Age", example = "20", required = true, position = 2)
    private int age;
}
package com.projectrespite.soapuidemo;

import org.springframework.stereotype.Repository;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Repository
public class PersonRepository {

    public List getPeople(){

        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject("http://localhost:8081/external/v1/people", List.class);
    }
}
package com.projectrespite.soapuidemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ExternalApiService {

    @Autowired
    private PersonRepository repository;

    public List getPeople(){

        List people = repository.getPeople();
        people.add(new Person("f95db0bc-7f8a-493c-95d5-0af08f38a10a", "箱崎花子", 30));

        return people;
    }
}
package com.projectrespite.soapuidemo;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/v1")
@Api(value = "/api/v1", tags = "Get people data")
public class ExternalApiController {

    @Autowired
    private ExternalApiService service;

    @GetMapping("people")
    @ApiOperation(value = "get people data", notes = "API returns all people data.")
    public List getPeople(){

        return service.getPeople();
    }
}

外部 API から JSON データを取得し、要素を追加した上でレスポンスを返します.

package com.projectrespite.soapuidemo;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.VendorExtension;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket swaggerSpringMvcPlugin() {

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("SoupUI Demo API")
                .select()
                .paths(paths())
                .build()
                .apiInfo(apiInfo())
                .directModelSubstitute(OffsetDateTime.class, java.util.Date.class)
                .directModelSubstitute(LocalDate.class, java.sql.Date.class)
                .protocols(new HashSet(Arrays.asList("http", "https")));
    }

    private Predicate paths() {
        return Predicates.or(Predicates.containsPattern("/api"));
    }

    private ApiInfo apiInfo() {

        return new ApiInfoBuilder()
                .title("SoapUI Demo API")
                .description("This is SoapUI Demo API.")
                .version("v1")
                .termsOfServiceUrl("https://www.project-respite.com/api/terms")
                .contact(new Contact("Project Respite",
                        "https://www.project-respite.com",
                        "email@project-respite.com"))
                .license("API LICENSE")
                .licenseUrl("https://www.project-respite.com/api/license")
                .extensions(new ArrayList())
                .build();
    }
}

Swagger ファイルから Driver を生成したいので、Swagger 定義を追加しました.

 

Swagger ファイル取得

Swagger UI から Swagger ファイルを取得します.
http://localhost:8080/v2/api-docs?group=SoupUI%20Demo%20API

 

SoapUI の操作

ここから SoapUI の操作を行なっていきます.

まずはプロジェクトを作成します.

 

Swagger ファイルをインポートします.

 

ファイルを選択します.

 

Swagger ファイルから Driver を作成できました.

 

次に Stub を作成します.

REST の Mock Service を作成します.

 

Stub のホスト名およびポートを指定します.

 

Mock Action を作成します.

 

メソッドとエンドポイントを指定します.

 

レスポンスを作成します.

 

Stub として返したいレスポンスを指定します.

 

最後にテストケースを作成します.

まずはテストスイート(複数のテストケースの単位)を作成します.

 

テストケースを作成します.

 

次に Stub を起動するスクリプトを作成します.
スクリプトは Groovy で書きます.

 

スクリプトを記述します.

testRunner.testCase.testSuite.project.getRestMockServiceByName("External API").start()

 

API 呼び出し(Driver)を作成します.

 

Driver 定義から呼び出したい API を選択します.

 

左上のプラスボタンからアサーションを追加します.

 

JSON Path Match を選択します.

 

配列の1番目の要素の id 属性にアサートします.

 

テスト実行

まずは GUI から実行してみます.

テストが正常に実行されました.

 

テストが失敗するとこのように表示されます.

 

次に shell から実行してみます.

shell で実行する場合、/Users/USER_NAME/.soapuios/plugins を参照できないバグがあるのか、エラーが出てしまいますので、plugins を何かしらにリネームすることをおすすめします.
How to resolve Auto Discovery Method Factory Error or Auto Import Method Factory in your newly created Soap UI-Jenkins Job?

$ export PATH=/Applications/SoapUI-5.5.0.app/Contents/java/app/bin:$PATH

$ testrunner.sh "SoapUI-Demo-soapui-project.xml"
================================
=
= SOAPUI_HOME = /Applications/SoapUI-5.5.0.app/Contents/java/app
=
================================
SoapUI 5.5.0 TestCase Runner
20:40:36,514 INFO  [DefaultSoapUICore] initialized soapui-settings from [/Users/aa367474/soapui-settings.xml]
20:40:38,024 INFO  [PluginManager] 0 plugins loaded in 5 ms
20:40:38,024 INFO  [DefaultSoapUICore] All plugins loaded
20:40:40,509 INFO  [WsdlProject] Loaded project from [file:/Users/aa367474/Desktop/SoapUI-Demo-soapui-project.xml]
20:40:40,521 INFO  [SoapUITestCaseRunner] Running SoapUI tests in project [SoapUI Demo]
20:40:40,522 INFO  [SoapUITestCaseRunner] Running Project [SoapUI Demo], runType = SEQUENTIAL
20:40:40,542 INFO  [SoapUITestCaseRunner] Running SoapUI testcase [TestCase]
20:40:40,553 INFO  [SoapUITestCaseRunner] running step [Mock Start Script]
20:40:41,428 INFO  [JettyMockEngine] Started mockService [External API] on port [8081] at path [/external]
20:40:41,432 INFO  [SoapUITestCaseRunner] running step [REST Request]
20:40:41,707 DEBUG [HttpClientSupport$SoapUIHttpClient] Attempt 1 to execute request
20:40:41,707 DEBUG [SoapUIMultiThreadedHttpConnectionManager$SoapUIDefaultClientConnection] Sending request: GET /api/v1/people HTTP/1.1
20:40:41,917 DEBUG [SoapUIMultiThreadedHttpConnectionManager$SoapUIDefaultClientConnection] Receiving response: HTTP/1.1 200 
20:40:41,924 DEBUG [HttpClientSupport$SoapUIHttpClient] Connection can be kept alive indefinitely
20:40:42,326 INFO  [SoapUITestCaseRunner] Assertion [ID Match 1] has status VALID
20:40:42,327 INFO  [SoapUITestCaseRunner] Assertion [Name Match 1] has status VALID
20:40:42,328 INFO  [SoapUITestCaseRunner] Assertion [Age Match 1] has status VALID
20:40:42,328 INFO  [SoapUITestCaseRunner] Assertion [ID Match 2] has status VALID
20:40:42,329 INFO  [SoapUITestCaseRunner] Assertion [Name Match 2] has status VALID
20:40:42,329 INFO  [SoapUITestCaseRunner] Assertion [Age Match 2] has status VALID
20:40:42,329 INFO  [SoapUITestCaseRunner] running step [Mock Release Script]
20:40:42,347 INFO  [JettyMockEngine] Stopped MockService [External API] on port [8081]
20:40:42,350 INFO  [SoapUITestCaseRunner] Finished running SoapUI testcase [TestCase], time taken: 1254ms, status: FINISHED
20:40:42,351 INFO  [SoapUITestCaseRunner] Project [SoapUI Demo] finished with status [FINISHED] in 1821ms

shell からもテストが実行できました.

 

まとめ

今回は SoapUI を使ってみました.

繰り返しになりますが、以下のような利点から非常に実用的なツールかと思います.

  • Windows/Mac/Linux で実行可能
  • REST と SOAP の両方に対応している
  • HermsJMS をインストールすれば MQ も使える
  • API Driver/Stub の両方の役割をこなせる
  • テストケースを設定してアサーションできる
  • shell/bat で起動できる

shell で実行できることからも、自動テストにも組み込めるかと思います.
とても便利なツールですね!

 

以上です.