【アプリ開発】テスト用APIを使ってレイアウトを確認する方法

はじめに

この記事ではアプリ開発におけるテスト工程についてのハンズオンと解説を行います。

  • テストって何をすればいいの?
  • 適当に文字数字をいろんなパターンで入力を試せばいいだけじゃないの?
  • やることはなんとなく分かるけどどうやるの?

という方は多いのではないでしょうか?

アプリのテストは自分で適当にデータを放り込むよりも、ある程度形式に沿ったランダムなデータを入れるほうが様々なパターンに対応できるため”良し”とされています。

今回はダミーデータを生成するJSONPlaceholderというAPIを使って、自作アプリ(React.jsで開発)のテストを行う方法を紹介していきたいと思います。

また、この記事では更にAPIとの接続方法についても解説しているため、ぜひご自身のアプリ開発に役立ててみて下さい。

本記事ではReact.jsやライフサイクルメソッドを使用します。
もし「何それ?よく分からないよ」という方は、以下の記事を先に読むと理解が深まるためオススメしています。

Reactの環境構築や基本的な文法等を紹介しています。

https://code-ship-blog.wemotion.co.jp/technology/react%e3%81%ae%e9%96%8b%e7%99%ba%e7%92%b0%e5%a2%83%e3%82%92docker%e3%81%a7%e6%95%b4%e3%81%88%e3%82%88%e3%81%86/
https://code-ship-blog.wemotion.co.jp/technology/react-js%e3%82%92%e5%a7%8b%e3%82%81%e3%82%88%e3%81%86%ef%bc%9a%e5%8b%95%e3%81%8b%e3%81%97%e3%81%aa%e3%81%8c%e3%82%89%e4%bb%95%e7%b5%84%e3%81%bf%e3%82%92%e7%9f%a5%e3%82%8d%e3%81%86/
https://code-ship-blog.wemotion.co.jp/technology/%e3%80%90react%e3%81%ae%e8%a8%ad%e8%a8%88%e3%82%92%e5%ad%a6%e3%81%b6%e3%80%91%e3%83%a9%e3%82%a4%e3%83%95%e3%82%b5%e3%82%a4%e3%82%af%e3%83%ab%e3%82%92%e7%9f%a5%e3%82%8d%e3%81%86/

目次

使用バージョン

ES6以降の構文(アロー関数、クラス等)を使用します。

node12.13.1
npm6.12.1
React16.12.0
axios0.19.0

JSONPlaceholderとは

JSONPlaceholderとはテストやプロトタイピングをするためにダミーデータを提供してくれるREST APIです。
様々なシチュエーションを想定したデータが既に用意されています。

JSONPlaceholder is a free online REST API that you can use whenever you need some fake data.
It’s great for tutorials, testing new libraries, sharing code examples, …

このAPIでは特定のURLにアクセスすることでJSON形式のデータを返してくれます。

JSONとは?

【何が違う?】データ形式(CSV, XML, JSON)の特徴を知ろう

今回はその中でも記事の投稿を想定したpostsの100件のダミーデータを使用します。
https://jsonplaceholder.typicode.com/postsにアクセスすると元となるデータがたくさんあることが確認できると思います。
今回はこのデータをReactアプリケーションで受け取り表示させるというテストをしてみたいと思います。

JSONPlaceholderではposts以外にも全部で6種類のデータが用意されています。
興味がある方は公式ページをご確認下さい。

データの種類データ数
/posts100 posts
/comments500 comments
/albums100 albums
/photos5000 photos
/todos200 todos
/users10 users

今回使用するReactアプリケーション

まずはテストを行うために簡単なアプリケーションを使用します。
react-create-appでアプリケーションの立ち上げを行った後、srcの以下のファイルを定義します。

ファイル構造:srcフォルダ内

src/
    ├── components
    │       └── Post
    │               ├── Post.js
    │           └── Post.css
    ├── container
    │       └── Blog
    │               ├── Blog.js
    │           └── Blog.css
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── serviceWorker.js

以下のファイルを編集します。

// app.js
import React, { Component } from "react";

import Blog from "./containers/Blog/Blog";

class App extends Component {
  render() {
    return (
      <div className="App">
        <Blog />
      </div>
    );
  }
}

export default App;
// components/Post/Post.js
import React from "react";

import "./Post.css";

const post = props => (
  <article className="Post">
    <h1>{props.title}</h1>
    <div className="Info">
      <div className="Author">{props.author}</div>
    </div>
  </article>
);

export default post;
/* components/Post/Post.css */
.Post {
  width: 250px;
  padding: 16px;
  text-align: center;
  border: 1px solid #eee;
  box-shadow: 0 2px 3px #ccc;
  margin: 10px;
  box-sizing: border-box;
  cursor: pointer;
}

.Author {
  margin: 16px 0;
  color: #ccc;
}
// containers/Blog/Blog.js
import React, { Component } from "react";

import Post from "../../components/Post/Post";
import "./Blog.css";

class Blog extends Component {
  state = {
    posts: [
      {
        id: 1,
        title: "TEST1",
        author: "Yui"
      },
      {
        id: 2,
        title: "TEST2",
        author: "Miyu"
      }
    ]
  };

  render() {
    const posts = this.state.posts.map(post => {
      return <Post key={post.id} title={post.title} author={post.author} />;
    });

    return (
      <div>
        <h1>Post List</h1>
        <section className="Posts">{posts}</section>
      </div>
    );
  }
}

export default Blog;
// containers/Blog/Blog.css
h1 {
  text-align: center;
}

.Posts {
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
  width: 80%;
  margin: auto;
}

このアプリでは以下のように画面が表示されます。

rendering1

この画面にPostデータをもっとたくさん入れて表示がどうなるか確認したいことがあると思います。
ただしそのままデータを増やすためには、データベースを定義したり、stateにたくさんサンプルを書いたりする必要があるので少し厄介です。

JSONPlaceholderでは簡単にたくさんのPostデータを入れて表示を確認できます
次で実際にデータにアクセスしたいと思います。

ダミーデータをレンダリングする

APIにアクセスするために準備

JSONPlaceholderを使うためには、axiosというライブラリが便利です。

axiosとはブラウザやnode.jsで動くPromiseベースのHTTPクライアントです。
axios自体はJavaScriptで書かれているので、簡単にReactアプリにも組み込むことができます

詳しくは以下の記事が有用です。
興味があれば読んでみると良いと思います。

[axios] axios の導入と簡単な使い方

まずはnpmでインストールしましょう。

$ npm install axios

これで準備完了です。

データを受け取る

データを受け取るにはaxiosのgetメソッドを使って簡単に受け取れます。
コンポーネントがレンダリングされた後にネットワークへのリクエストに使用するcomponentDidMount()を使います。

// Blog.js
import React, { Component } from "react";
import axios from "axios";

import Post from "../../components/Post/Post";
import "./Blog.css";

class Blog extends Component {
  state = {
    // 後にデータが入るので空にする
    posts: []
  };

    // 以下のURLにアクセスし、取得したデータをコンソール上に出力
  componentDidMount() {
    axios
      .get("https://jsonplaceholder.typicode.com/posts")
        // 非同期通信なのでPromiseを使う
      .then(response => {
      console.log(response);
    });
  }

  render() {
    const posts = this.state.posts.map(post => {
      return <Post key={post.id} title={post.title}/>;
    });

    return (
      <div>
        <h1>Post List</h1>
        <section className="Posts">{posts}</section>
      </div>
    );
  }
}

export default Blog;

axiosのgetメソッドでURLを指定しresponseとして受け取り、そのデータをコンソール上に出力してみます。

rendering2

responseにはHTTPメソッドやURLの情報が入っているconfigやステータスコードが入っているrequestなど様々なものがあります。
その中のdataにはArray型でuserIdやtitleなど画面上に表示させたいものが入っています。

ではこのdataの中身を画面に表示させてみましょう。

このデータを画面に表示させるには、setState()を用いてresponse.dataをstateのpostsに更新します。

// Blog.js

...
componentDidMount() {
    axios.get("https://jsonplaceholder.typicode.com/posts").then(response => {
      this.setState({ posts: response.data });
    });
  }
...

これでデータをしっかり表示できました。

rendering3

ダミーデータを制御する

このダミーデータは必ずしもそのまま受け取るだけではなく、制御することもできます
具体的に受け取る数を制御したり、受け取ったデータを書き換える方法をご紹介します。

受け取るデータ数を制御する

受け取るデータ数を制御するためには、slice()メソッドを使用します。
slice()メソッドはArray型のデータを切り抜く事ができるので、自分の欲しいデータを指定し受け取る事ができます。

// Blog.js

...  
    componentDidMount() {
    axios.get("https://jsonplaceholder.typicode.com/posts").then(response => {
      // 4つのデータをpostsに定義して、stateを更新
      const posts = response.data.slice(0, 4);
      this.setState({ posts: posts });
    });
  }
...

これで指定した4つのデータだけが表示されます。

rendering4

受け取るデータに項目を追加する

今回受け取ったデータにはbody、id、title、userIdしか項目がありません。
他にも追加したい場合はどのようにすれば良いでしょうか。

追加するにはmap()メソッドを使用します。
map()メソッドは与えられた関数を配列の全ての要素に適応し、その結果から新たな配列を生成します。
このメソッドを使用し、dataにauthorを追加できます。

// Blog.js

...
    componentDidMount() {
    axios.get("https://jsonplaceholder.typicode.com/posts").then(response => {
      const posts = response.data.slice(0, 4);
      // map()でpostにauthorを割り当てる関数を適応
      const updatePosts = posts.map(post => {
        return {
          ...post,
          author: "Hashimoto"
        };
      });
      this.setState({ posts: updatePosts });
    });
  }
...

これですべての要素にauthorが追加されました。

rendering5

エラーを受け取る

このAPIを使っていると予期せぬエラーと遭遇する可能性があります。
そのエラーを捕まえるにはcatchメソッドを使用します。
エラーを捕まえておくとログに残したり、ユーザーの画面を変更したりすることができます。

以下はエラーを受け取った際に画面を変更するためのコードです。

// Blog.js

    ...
  state = {
    posts: [],
    selectedPostId: null,
    // errorハンドリング用のstate
    error: false
  };

    componentDidMount() {
    axios
      .get("https://jsonplaceholder.typicode.com/posts")
      .then(response => {
        const posts = response.data.slice(0, 4);
        const updatedPosts = posts.map(post => {
          return {
            ...post,
            author: "Hashimoto"
          };
        });
        this.setState({ posts: updatedPosts });
      })
        // errorをcatchを使って捕まえてstateを更新
      .catch(error => {
          // エラーをコンソール上に表示
          console.log(error);
        this.setState({ error: true });
      });
  }

    render() {
    // errorを画面に表示
    let posts = <p style={{ textAlign: "center" }}>Something went wrong!</p>;

    // errorがtrueじゃない時にpostを表示
    if (!this.state.error) {
      posts = this.state.posts.map(post => {
        return (
          <Post
            key={post.id}
            title={post.title}
            author={post.author}
            clicked={() => this.postSelectedHandler(post.id)}
          />
        );
      });
    }
    ...

これでAPIにエラーが生じた場合、ユーザーに画面上で知らせる事ができます。
例えばURLを変更してエラーを起こしてみます。

...
axios
    // 間違ったURL
    .get("https://jsonplaceholder.typicode.com/postssss")
...

変更後の画面にはSomething went wrong!と表示されます。
catchメソッドでエラーを捕まえて、stateがtrueに更新されました。

またコンソール上には以下のメッセージが書かれています。

Error: Request failed with status code 404
    at createError (createError.js:17)
    at settle (settle.js:19)
    at XMLHttpRequest.handleLoad (xhr.js:60)

詳細は割愛しますが、一番上にステータスコードが404になっています。
これは非常に有名なコードで該当のURLにページが存在しない時に返されます。

以前に書いたステータスコード等の通信にまつわる記事があります。
興味があれば読んでみて下さい。

【WEBプログラミング初心者必読】意外と知らないWEBの仕組み

このようにエラーを受け取り、結果をログに残したり表示する画面を変えたりすることもできます。

まとめ

今回はJSONPlaceholderを用いてテストを行う方法を紹介しました。

自分でデータを作らずにテストを行え、APIとの接続方法も学べるため勉強には非常に優良なツールです。

ぜひ皆さんも一度手を動かして実践してみてください。

React.jsに関する他の記事を見る↓↓

https://code-ship-blog.wemotion.co.jp/technology/%e3%80%90react%e3%80%91%e3%82%a2%e3%83%97%e3%83%aa%e9%96%8b%e7%99%ba%e3%82%aa%e3%82%b9%e3%82%b9%e3%83%a1%e8%a8%98%e4%ba%8b%e3%81%be%e3%81%a8%e3%82%81%ef%bc%81%e8%a8%80%e8%aa%9e%e3%83%bbfw%e3%81%ae/

参考URL

JSONPlaceholder

https://jsonplaceholder.typicode.com/

【何が違う?】データ形式(CSV, XML, JSON)の特徴を知ろう

http://bit.ly/3638Sek

[axios] axios の導入と簡単な使い方

https://qiita.com/ksh-fthr/items/2daaaf3a15c4c11956e9

初心者目線でAjaxの説明

https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9


おすすめ記事

https://code-ship-blog.wemotion.co.jp/technology/%e3%80%90%e3%83%97%e3%83%ad%e3%82%b0%e3%83%a9%e3%83%9f%e3%83%b3%e3%82%b0%e5%88%9d%e5%bf%83%e8%80%85%e5%bf%85%e8%aa%ad%e3%80%91%e8%aa%b0%e3%81%a7%e3%82%82%e3%81%a7%e3%81%8d%e3%82%8b%e3%82%a8%e3%83%a9/
https://code-ship-blog.wemotion.co.jp/technology/%e3%80%90%e4%bd%95%e3%81%8c%e9%81%95%e3%81%86%ef%bc%9f%e3%80%91%e3%83%87%e3%83%bc%e3%82%bf%e5%bd%a2%e5%bc%8fcsv-xml-json%e3%81%ae%e7%89%b9%e5%be%b4%e3%82%92%e7%9f%a5%e3%82%8d%e3%81%86/
https://code-ship-blog.wemotion.co.jp/technology/react-native-expo%e3%81%a7%e4%bd%9c%e3%82%8b3%e3%83%97%e3%83%a9%e3%83%83%e3%83%88%e3%83%95%e3%82%a9%e3%83%bc%e3%83%a0%e5%af%be%e5%bf%9c%e3%81%ae%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7/