Java はじめて26 〜JUnitでのテスト駆動型開発3: クラスの実装〜

今回は、JUnitでのテストケースから、テストをクリアできる様に実態クラスを作成します。
つまり、JUnitでテストケースを要件を満たす様に作成し、テスト実行後に緑色の表示がされる様にKozaManagerのメソッドを作成します。
ちなみに、今回の実装する時の要件は以下の様になっています。

  1. ファイルを操作するためのオブジェクトを作成する
  2. ファイルにデータをCSV形式で書き出す。
  3. ファイルが存在するのであれば、それを読み込みデータを保持する

オブジェクト指向での実装を行うので、どの様に考えれば良いか?の指針になれば良いと思います。

今までの概要

前回作成したのは、上の要件のうちの1(コンストラクタで実装)だけです、これはテストを実行する準備段階でもあるので、テストケースとしては「インスタンスがtargetに設定されていること」を確認するテストケースを作成しました。

public class KozaManager {
    /** ファイルへの書き出しクラス */
    private BufferedWriter write;
    /** ファイルの読み込みクラス */
    private BufferedReader read;

    /** コンストラクタ */
    public KozaManager() {
        // 操作するファイルを指定する
        File file = new File("resources/koza.csv");
        try {
            write = new BufferedWriter(new FileWriter(file));
            if (file.exists()) {
                read = new BufferedReader(new FileReader(file));
            }
        } catch (IOException ie) {
            ie.printStackTrace();
            System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
            System.exit(-1);
        }
    }

    /** デストラクタ */
    @Override
    protected void finalize() throws Throwable {
        write = null;
        read = null;
    }

    /**
     * データクラスを受け取り、CSVファイルを出力する(書き出しを行う)
     * @param data コーダー銀行のユーザー情報
     */
    public void dataOutput(Data data) {
    }
}

コンストラクタと、デストラクタを実装しています。
コンストラクタは、ファイル操作に必要なクラスのインスタンスを作成します。
デストラクタは作詞したインスタンスを解放します。

現状のテストコード

package jp.zenryoku.apps.atm.manage;

import static org.junit.Assert.assertNotNull;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import jp.zenryoku.apps.atm.data.Data;

/**
 * @author takunoji
 *
 * 2019/09/25
 */
public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;

    /**
     *  テストの初期化 
     *  各テスト実行前に起動する
     */
    @Before
    public void initClass() {
        target = new KozaManager();
    }

    /**
     * コンストラクタが起動したかどうかを確認する
     * テストケース
     */
    @Test
    public void testIsFile() {
        assertNotNull(target);
    }

    /**
     * ファイルにデータを出力し保存するテストケースです。
     */
    public void testFileCeate() {
    }
}

テストケース作成

今回の目玉になります、現状はから実装になっている「testFileCreate()」の実装を行いますが、これはテスト対象クラス(本来作成するクラス)がどう動けば良いか?を考えて作成する必要があります。

どう動けば良いか?

口座情報管理クラスで入力されたデータをファイルに出力する必要があるので単純に「データを受けて、それをCSVに変換してファイル出力する」処理を行うのがこのメソッドの処理になります。
まとめると以下の様になります。

  1. 入力としてDataクラス(口座情報クラス)を受ける
  2. 出力として、CSVファイルを出力する

ここで、KozaManagerクラスの他に「Dataクラス」が必要であることがわかります。これは前回作成しております
そして、CSVファイルを出力するのに、ヘッダー部分のテンプレート(CSVファイルのヘッダは固定値です)を出力する必要がありますので、これをKozaManagerに追加します。
現状では、以下の様なコードになります。

/**
 * 出力するCSVのヘッダー部分を作成する。
 * @return CSVヘッダーの文字列(カンマ区切り)
 */
private String createCSVHeader() {
    return "名前, パスワード";
}

/**
 * データクラスを受け取り、CSVファイルを出力する(書き出しを行う)
 * @param data コーダー銀行のユーザー情報
 */
public void dataOutput(Data data) throws IOException {
    StringBuilder build = new StringBuilder();
    build.append(this.createCSVHeader());
    build.append(data.getName() + ",");
    build.append(data.getPassword());
}

しかし、ここで注意したいのがCSVデータは大雑把に以下の様な形式になります。そして、サンプルデータを記載しておきます。
<形式>
1行目: ヘッダー
2行目以降: データ

名前, パスワード
テスト太郎, 1234
テスト花子, 2345
テストたくのじ, 3456

頭をひねる

ここで、元のクラスに色々と修正を加える必要が出てきました。追加する修正は以下のものです。

  1. ファイル出力する時のパス指定をハードコーディング(File file = new File("resources/koza.csv");)しているのを定数としてクラスに保持したい
  2. ファイルが出力できたか?のファイル存在チェックメソッドが欲しい

これらの修正を加えるのに、テストケースで「ファイルにデータをCSV形式で書き出す。」という処理は最後になります。
上の処理を確認するのにファイルの存在チェックなどが必要なので先にそちらの処理を作成する必要があるからです。

先にファイル存在チェック処理を作る

これをやる必要が出てきました。
これは次回やろうと思います。

でわでわ。。。



Java はじめて25 〜JUnitでのテスト駆動型開発2: テストケース作成〜

今回は、コンストラクタがエラーもなく正常に動いたかどうかの確認を行うところから始めます。

前回の復習

JUnitがなんなのか?に関して記載しました。
そして、このフレームワークはテスト(作成したコード)を実行してその動きが想定通りに動いたかどうか?を「assert」を使用して確認できるものです。

動かすためのポイント
junit.jarがビルドパスに設定されている

大まかに以下の様な形で実装します。

public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;

    /**
     *  テストの初期化 
     *  各テスト実行前に起動する
     */
    @Before
    public void initClass() {
        target = new KozaManager();
    }

    /**
     * コンストラクタが起動したかどうかを確認する
     * テストケース
     */
    @Test
    public void testIsFile() {
        assertNotNull(target);
    }
}

今回のテストケースを実装していますが、ポイントとしては、以下の点が挙げられます。

  1. @Beforeは書くテストケース(メソッド)が起動する前に動く
  2. @Testはテストケース(メソッド)に付ける

上のコードでは、コンストラクタが起動してフィールド変数「target」にインスタンスが設定されているかどうか?を判定します。targetにインスタンスが設定されていない=コンストラクタが起動している最中に例外(Exception)が発生して@Befireのついているメソッドの処理target = new KozaManager()の処理が終わらないうちに(targetに値が設定されないうちに)例外を受け取る処理が動くためです。

具体的に

下の動画を見てもらうとわかると思います。

この処理は、コンストラクタで例外を発生させています。

public KozaManager() {
    // 操作するファイルを指定する
    File file = new File("resources/koza.csv");
    try {
        write = new BufferedWriter(new FileWriter(file));
        if (file.exists()) {
            read = new BufferedReader(new FileReader(file));
            throw new IOException("テスト例外");
        }
    } catch (IOException ie) {
        ie.printStackTrace();
        System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
        System.exit(-1);
    }
}

ここでのポイントは例外が発生した後にアプリ(メインメソッドの処理)を強制終了しているのでJUnitの緑色とか茶色(エラー時)が表示されません。

補足

ちなみに、コードを実行した後にガベージコレクションでフィールド変数などが解放されるので実装しなくても大丈夫なのですが、テストケースでなんどもKozaMangerクラスのインスタンスを生成するのでC言語的にいうところの「デストラクタ」(コンストラクタの逆=インスタンスを削除する時)が動く時にフィールド変数を解放します。この処理を追加します。

/** デストラクタ */
@Override
protected void finalize() throws Throwable {
    write = null;
    read = null;
}

こうすることで、メモリに余計なもの(オブジェクト)を保存しないので、処理が快適に動きます。

テストケースの実装

ここからが本当のテスト駆動型開発です、現状はコンストラクタを作成していますが、説明のため。。。言い訳くさい。。。

とりあえず、以下の様に考えます。

「このクラスで要件を満たすために何をしたら良い?」

自分の答えは以下の通りです。

  1. ファイルを操作するためのオブジェクトを作成する
  2. ファイルにデータをCSV形式で書き出す。
  3. ファイルが存在するのであれば、それを読み込みデータを保持する

項目1はコンストラクタで実現しているので。今回は2のファイルにCSVデータを書き出す。を実装しようと思います。
設計としては以下の様にします。
<設計>

  1. ファイルの書き出しを行うメソッドはdataOutputという名前にする
  2. 書き出しを行うときはCSV形式で出力する
  3. データを受け取るときは「Dataクラス」で受け取る

この設計から行くと作成するメソッドは「dataOutput」で引数は「Dataクラス」になります。
そして、返却値は「なし=void」です。

なので、テストケースとしては以下の様なコードになります。

/**
 * ファイルにデータを出力し保存するテストケースです。
 */
public void testFileCeate() {
    Data data = new Data("名前", "パスワード");
    target.dataOutput(data);
}

この様に実装しましたが、「Data」クラスも「dataOutput」メソッドも存在しないのでエラーになります。
なのでこれを「スタブ=空実装(名前だけ、形)」で作成します。そして、作成するDataクラスは以下の様な形で実装します。
これはデータクラスで、内容はフィールドと「Getter(ゲッター)」と「Setter(セッター)」のみになります。

public class Data {
    /** ユーザー名 */
    private String name;
    /** パスワード */
    private String password;
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }
    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }
}

そして、今回作成するメソッドをスタブで作成します。
さらに、データクラスをインポートします。
import jp.zenryoku.apps.atm.data.Data;

<スタブメソッド>

/**
 * データクラスを受け取り、CSVファイルを出力する(書き出しを行う)
 * @param data コーダー銀行のユーザー情報
 */
public void dataOutput(Data data) {
}

次回は、このスタブで要件を満たすことができるかどうか考え、必要な実装を行いたいと思います。

でわでわ。。。



Java はじめて24 〜JUnitでのテスト駆動型開発1〜

今回はJUnitを使用して開発を行います。と言っても今まで作成していたクラスの実装を行うというだけです。
まずは、テスト仕様を作成するのですが、これは前回作成したので割愛します。

JUnit

前回、JUnitが動くことを確認するプログラムを作成しました。
なので今回は、設計部分から。。。早い話が「どー動けば良いか?」を考えることから実装を始めたいと思います。

テスト駆動

テストケース(どう動くか?)を作成することから初めていきます。
<仕様部分>
指定のファイルが存在すれば、ファイルを参照することができ、ファイルが存在しなけでば、ファイルを作成する。

今回はこの部分のみを実装します。初めにテストクラスの構成ですが、書くテストケースを実行する前にテスト対象クラスのインスタンスを新規で作成します。@Beforeアノテーションをつけてやるとそのメソッドはテストケースの実行前に起動します。

@Before
public void initClass() {
    target = new KozaManager();
}

そして、問題のテストケースですが、プログラムレベルでまずは、ファイルの読み書き用クラスのインスタンスが取得できないと話にならないのでその起動確認を行います。
そして、assertを使用できることがJUnitの魅力でもあります。

Assertとは

JUnitのフレームワークで使用できる想定通りに動いたか確認するためのメソッドです。
使い方は以下の通りです。

/**
 * コンストラクタが起動したかどうかを確認する
 * テストケース
 */
@Test
public void testIsFile() {
    assertNotNull(target);
}

上のコードでは、Assertクラスを「static import」して使用しています。のでメソッドを直接呼ぶことができます。assertNotNullなどの確認用メソッドです、上の場合だとtargetがNullの時にエラーを吐きます。他にもassertEqualsがありこれは

assertEquals(value, "aaa");

の様に使用し値が等しくない時にエラーを吐きます。

メソッドの中で完結しないのですが、@Beforeでコンストラクタを起動しているので、テストケースとしては実行済みになりますので、インスタンスがtargetの中に入っているかどうかの確認を行います。

そして、問題の「ファイルを読み込むためのクラス」は以下の様に処理を行っています。下のクラスはテスト対象クラスです。

/** コンストラクタ */
public KozaManager() {
    // 操作するファイルを指定する
    File file = new File("resources/koza.csv");
    try {
        write = new BufferedWriter(new FileWriter(file));
        if (file.exists()) {
            read = new BufferedReader(new FileReader(file));
        }
    } catch (IOException ie) {
        ie.printStackTrace();
        System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
        System.exit(-1);
    }
}

とりあえずは、このテストがうまくいく様に作成します。
しかし、今回のはコンストラクタを起動するだけ、しかもテストケースと呼べる様なものではないのでよろしくはないのですが、コンストラクタで必要なインスタンスなどを作成しないことには何も始まらないのでこの様になりました。

ポイント

  1. テストケースを作成する前の準備
  2. コンストラクタでテストケースに関連する部分を含むがテストを行ったことにはしない

とりあえずはここまでにします。

でわでわ。。。



Java はじめて23 〜テスト駆動型開発を行う〜

今回は、JUnitを使用して以下のことを実行します。

  1. テスト仕様の作成
  2. テストケース(コード)の作成
  3. 本番のクラスを作成する

テスト駆動型開発

世間ではいろいろなことが言われていますが、シンプルに「テスト仕様から作成する方法」という意味で記載しています。
シンプルに「テスト仕様から作成する」ということはどいうことかに関して、ちょいと記載します。
早い話が、「このクラスはどう動けば良いの?」から考える実装方法だと思っています。「テスト駆動型開発」という言葉を作った人間ではないので、この様な書き方になります。

ちなみに、JUnitが使用できるかどうかに関しては下の様に「JUnit」がワークスペース上に表示されていれば大丈夫です。
例外として、存在するけど動かないということもありますが、それはエラーが出力され、コンソールに「XXXException」と表示されるのでそれをインターネットで調べてみると解決します。
ちなみに、自分の場合は以下の様にアノテーションをつけてやると動きました。Springなどのフレームワークを使用している場合は、それぞれのテストさ癖方法があるのでそちらを参照されたし。。。JUnit5を使用している場合です

@@RunWith(JUnit5.class)
public class MyTest {
テストケース
}

テスト仕様を考える

前回作成した、仕様と設計より「口座管理クラス」を作成することにしました。この口座管理クラスはファイルを使用してデータを保存したり、更新したりします。

本番で使用するクラスとテストクラスを分けるために以下の様に完全クラス名は同じだけどルートフォルダが違う形で作成します。
今回作成するクラスは、KozaManagerクラスです。

そして、テストクラスは下の様に「test」フォルダいかに作成します。

テスト仕様

テスト仕様というと堅苦しい感じがしますが、早い話が「どう動けば良いか?」を決めるところです。ちょっと慎重にいきます。
しかし、今回はファイルに出力するだけなので深く考えません。

CSVファイルを作成

CSVファイルはカンマ区切りのテキストファイルです。

項目1, 項目2 , ....
データ1, データ2, ...

この様な形で、データを保存します。今回は、口座情報なのと最小限度の情報を登録するので、ユーザーとパスワード、預金額をデータとして保存します。

早速テストが動くか確認します。

public class KozaManagerTest {
    /** テスト対象クラス */
    private KozaManager target;

    /**
     *  テストの初期化 
     *  各テスト実行前に起動する
     */
//  @Before
//  public void initClass() {
//      target = new KozaManager();
//  }

    /**
     * isFileメソッドのテストケース
     */
    @Test
    public void testIsFile() {
        System.out.println("Hello JUnit");
    }
}

ちなみに、コンストラクタを動かしたくないので今はコメントアウトしています。まずは、テストが動くか確認します。

テストを実行するときは下の様に表示されます、

動かしてみた結果です。

今日はこの辺で失礼します。

でわでわ。。。



Java はじめて22 〜オブジェクト指向的分析、アプリ拡張の実装とテスト〜

今回は、作成した仕様、設計した機能に関して実装していこうと思います。前回作成した仕様、機能に関しては以下にまとめておきます。

仕様と機能

仕様
1. 口座開設している人の情報を保存する
2. 口座を持っている人を特定する
3. 口座の預金額と口座を持っている人を一意にする

そして、上の仕様をいっぺんに実装するのは自分の頭では追いつかないので諦めて、1つずつ考えていこうと思います。
まずは、仕様1「口座を開設している人の情報を保存する」に関して、ますは「口座を開設する」機能が必要です。というかこれがないとこれ以降の機能が実装できません。
なぜか?口座が作成されないと口座を開設している人が追加できない、つまり口座を持っている人が0人のまま。。。
ということになるからです。

機能
1. 口座の情報をファイルに保存する
2. 口座の情報は「名前」と「パスワード」とする
3. ファイルには、カンマ区切りの「CSV」形式でデータを登録する

ファイル入出力の実装

やっと実装にたどり着きました。ここまで来た方おめでとうございます。
たまたま、このページに来た方、ブログなのでいつでもこれ以前の記事を参照できます。下のリンクで一覧が見れます。よかったらチャレンジしてみてください。
Java はじめて 学習フロー

java.io.Fileクラス

このクラスを使用してファイルの操作を行います。ここで注意して欲しいのは「Fileクラス」はファイル、ディレクトリ(フォルダ)を表現するクラスであり、「入出力」は別のクラスを使用する必要があるというところです。

java.io.BufferedWriterクラス

上のFileクラスと併用してこのクラスでファイルへの書き出しを行います。四の五の言っても意味がないので、コードから見ていきます。まずは、上の2クラスの作成(生成)を行います。口座を操作、管理するクラスは「KozaManager」と名付けました。

package jp.zenryoku.apps.atm.manage;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 口座の管理(登録、更新、削除)を行うクラス
 * @author takunoji
 *
 * 2019/09/21
 */
public class KozaManager {
    /** ファイルへの書き出しクラス */
    private BufferedWriter write;
    /** ファイルの読み込みクラス */
    private BufferedReader read;

    /** コンストラクタ */
    public KozaManager() {
        // 操作するファイルを指定する
        File file = new File("resources/koza.csv");
        try {
            write = new BufferedWriter(new FileWriter(file));
            if (file.exists()) {
                read = new BufferedReader(new FileReader(file));
            }
        } catch (IOException ie) {
            ie.printStackTrace();
            System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
            System.exit(-1);
        }
    }
}

コンストラクタで、ファイルの書き出し、読み込みクラスを作成(生成)しています。注意としては、ファイルの読み込み時にファイルが存在していない可能性があるので「ファイルが存在しているならばif (file.exists()) {...}」リーダー(BufferedReader)を作成しています。
そして、何かしらの例外(ファイル入出力関連の例外)があった場合に備えtry { ... } catch(IOException ie) { ... }で例外処理を作成しています。

テスト駆動開発

私事ではありますが、「テスト」に対しての認識を共有したく思い「テスト駆動開発」でやっていきたいと思っています。
このテスト駆動型開発は「仕様を明確にする」のが至上命令で、「どーゆー風に動けば良いか?」を一番に考えます。。。なんか当たり前な話に聞こえますが、「当たり前」はできていなくてはならない(実際難しい。。。)ことです。まぁ頑張りましょう(笑)
というわけで、テストケースから作成していきます。詳細に関してはこちらを参照してくださいwwww。

余談

テスト駆動型開発がオススメな理由、というか自分の考えですが、テストケースがちゃんとできていれば「テストが通れば、実装内容はほぼ自由」で良いことになります。そして、リファクタリングをするにも「テストが通ればデグレートの心配はない」ということになります。しかもテストは自動で実行できるツール(Mavenなど)があるので、テスト処理を流して帰宅するのもおっけーです(作業時間に余裕が必要ですが。。。)

テストケースの作成

今回のテストはファイルの読み込みと書き出しができることを確認するためのケースを作成します。そして、このテストケースを通る(エラーが出ない)のであれば実装は完了になります。
なので考えなくてはいけないのがいかになります。

テスト仕様

  1. 操作するファイルが存在しなければ、作成し、存在すればそのまま参照する
  2. 同様にファイルへ(口座の)ユーザー情報をファイル(CSV形式)への出力、保存ができること
  3. 同様にファイルの読み込みができること
  4. 読み込んだファイルのデータをユーザー情報クラスで取得できること(同様にファイルへ出力できること)

上の4項目を満たすようなクラスができれば、現状はおっけーです。今回は、ここまでにします。

でわでわ。。。



Java はじめて21 〜オブジェクト指向的分析、アプリの拡張をする〜

今回は、コーダー銀行(コンソールアプリ)の拡張をします。つまりは、機能追加をします。

今までに、以下の内容を実施(記載)してきました。

  1. 作成したアプリの修正
  2. チェック処理クラスの追加

現状の処理としては、コーダー銀行への「入金」と「引き出し」ができるだけであまり役に立たなそうです。
なので、今度はもっと銀行らしく口座の開設機能を作る方向へ向けて機能拡張を考えて見たいと思います。

オブジェクト指向的分析

機能拡張をするときには「どこを拡張するか?」つまり「コードのどの部分をどのように修正するか?」という疑問に答える必要があります。
そこで、今までの作成したものを、確認がてらに見直して「修正ポイント」を探し、拡張するための設計を行いたいと思います。

まずは仕様

「どのようなことを実現するか?」を初めに考える必要があります。そして、今回の場合は「コーダー銀行の口座開設」が実現したいことなので、口座の開設のために必要なことを考えます。

  1. 口座開設している人の情報を保存する仕組みが必要
  2. 口座を持っている人を特定する仕組みが必要
  3. 口座の預金額と口座を持っている人を一意にする仕組みが必要

大まかにこんな感じだと思います。あまり細かいと複雑になりクラス設計がややこしくなるので、今回は学習目的なのでこの程度の仕様にしておきます。

現状の構成を把握

今動いているアプリは下のような感じで動きます。

クラスの構成としては以下の3つがあり、それぞれ「メイン処理」と「預金額管理」、「チェック処理」を担当するクラスになっています。※実際のソースはGitにアップしています。ちなみに、パッケージは下のようになっています。
「jp/zenryoku/apps/atm」

実装ポイント

口座の開設処理を行う前に、現状では「入金」と「引き出し」の機能しかありません。この場合だとコーダー銀行の口座は一人だけで誰でも引き出しができてしまうので安全ではありません(学習ようなので。。。)。1つずつ問題を解消していきますが、まずは口座を開設する機能が必要です。
ユーザー(口座保持者)と口座を結びつける処理などは、後で考えます。

自分は、1度に複数のことを考えることができません。。。

口座開設処理

通常の業務アプリ(〜銀行さんとかで使用しているもの)は、ものすごい人数が利用できるように「データベース(DB)」を使用しています。しかし、DBを使用する前に、「ファイル操作」に関して理解する必要があるのでそのように実装いたします。

ファイルに口座情報を保存する

今回作成する「コーダー銀行アプリ」は学習ようなので本物のように個人情報をてんこ盛りにしたような実装はしません。単純に作成するため必要最低限の項目のみを使用します。それは以下に示します。

  1. 名前(ヘボン式のローマ字)
  2. パスワード

そして、今回実装する処理としては。。。

ファイルにデータを登録する処理

この機能を実装するのには、今までのやり方からすると「ファイルにデータを登録する役割」のあるクラスを作成し、そのクラスに処理を実装する、ということになります。

これもオブジェクト指向の基本になります。くどいようですが、「役割分担をちゃんとやりましょう」ということです。

設計を行う

ここまできたら、察しの良い人は「ファイル操作クラスを作るんでしょ?」と思うかもしれません。その通りでございます。

そして、実装する内容は以下のようになります。

  1. ファイル操作クラスを作成(実装)する
  2. ファイル操作クラスを使用する部分を追加(修正)実装する

補足

自分で作成したものは「自分で好きなように改造できる」ので、想像を膨らませて「面白いもの」を作ってみるのも良い学習になります。

まとめ

上記の内容をまとめると以下のようになりました。

仕様
1. 口座開設している人の情報を保存する
2. 口座を持っている人を特定する
3. 口座の預金額と口座を持っている人を一意にする
機能
1. 口座の情報をファイルに保存する
2. 口座の情報は「名前」と「パスワード」とする
3. ファイルには、カンマ区切りの「CSV」形式でデータを登録する

次回は、ここで作成した仕様に基づき実際にコードを作成(実装)してみようと思います。

でわでわ。。。

div visible=“hidden”>

<ins class="adsbygoogle"
style="display:block"
data-ad-format="fluid"
data-ad-layout-key="-6t+ed+2i-1n-4w"
data-ad-client="ca-pub-7367785927150581"
data-ad-slot="3284986237">