Fippiyのプログラム学習内容アウトプットBlog

日々の学習内容をアウトプットして振り返りを実施する。

Laravel開発、ISBNレコードを複数登録する【5】テストに失敗し問題のあったコードを修正する

複数ISBN登録に対するテストを実装しましたが、最後のテストに失敗しました。

処理を確認する中で、実際に処理手順に問題があることに気づいたので修正を実施していきます。

最終目的

ISBNコードを10件まで1フォームから入力して登録できるようにする

今回の目的

  • ISBN複数登録テストにて失敗した内容を解消する

なぜやるか

  • エラー処理が正常にできていない状態となっているので、問題解消して正常動作するサイトとするため

やりたいこと

  • テスト失敗の原因をつきとめる
  • 問題のある処理を修正する

やったこと

テストに失敗する場所を特定する

実施内容

問題はなにか

テスト内容の再確認

API検索で書籍情報がなかった場合、結果表示画面が正常に出力されずにエラー表示となっていました。

# ~/tests/Feature/BookdataTest.php

// 複数isbn登録エラー、検索結果なし
public function test_someIsbnCreate_ng_findNotData(){
 //// ユーザー生成
 $user = factory(User::class)->create(); // ユーザーを作成
 $this->actingAs($user); // ログイン済み
 $this->assertTrue(Auth::check()); // Auth認証済であることを確認
 // 登録
 $isbn0 = 1234567890123;
 $isbns = [
  'isbn0' => $isbn0,
 ]; // 新規登録コード
 $bookpath = 'book/isbn_some';
 $response = $this->from($bookpath)->post($bookpath, $isbns); // isbn情報保存
 $response->assertSessionHasNoErrors(); // エラーメッセージがないこと
 $response->assertStatus(200); // 200ステータスであること

 $response->assertSeeText('ISBNコード登録結果'); // 登録結果ページが出力されていること
 $response->assertSeeText($isbn0); // 入力番号が反映されていること
 $response->assertSeeText('該当するISBNコードは見つかりませんでした。');
 // 取得できていない旨のメッセージが表示されること
}

1234567890123というコードは実際にはありません、13桁入力が分かりやすい…というわけで適当に入力しているものです。

エラーとなるISBN指定をすると、「該当するISBNコードは見つかりませんでした。」と表示される…はず。

 

# terminal

$ vendor/bin/phpunit tests/Feature/BookdataTest.php

PHPUnit 7.5.7 by Sebastian Bergmann and contributors.

...................F  20 / 20 (100%)

Time: 2.98 seconds, Memory: 32.00 MB

There was 1 failure:

1) Tests\Feature\BookdataTest::test_someIsbnCreate_ng_findNotData

Expected status code 200 but received 500.

Failed asserting that false is true.

/Users/fiervega/php_projects/book-property-management/vendor/laravel/framework/src/

 Illuminate/Foundation/Testing/TestResponse.php:133

/Users/fiervega/php_projects/book-property-management/tests/Feature/

 BookdataTest.php:503

FAILURES!

Tests: 20, Assertions: 155, Failures: 1.

失敗しました。エラーコード500となっており、そもそも正常に結果表示ビューが出力できていません。

 

実際に処理して確認する 

失敗原因を探るために、実際に動作させて確認します。

f:id:Fippiy:20190518110041p:plain

フォームによる入力

API検索によって書籍情報がないと分かっているISBNコードを入力して送信します。

 

結果として、実際にエラー画面が表示されました。

ErrorException (E_ERROR)
Undefined index: title (View: ~/php_projects/book-property-management/resources/views/book/isbn_result.blade.php)
Previous exceptions
  • Undefined index: title (0)

 

さらに詳細情報を確認すると…。

  1. </div>
  2. <?php if(isset($answer['result'])): ?>
  3. <div class="isbn-result__box--bottom">
  4. <div class="isbn-result__box--image">
  5. <?php if(isset($answer['result']['cover']) == null): ?>
  6. <img src="../image/no-entry.jpg">
  7. <?php else: ?>
  8. <img src="<?php echo e($answer['result']['cover']); ?>">
  9. <?php endif; ?>
  10. </div>
  11. <div class="isbn-result__box--title"><?php echo e($answer['result']['title']); ?></div>
  12. </div>
  13. <?php endif; ?>
  14. </div>
  15. </div>
  16. <?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
  17. </div>
  18. </div>
  19. <?php $__env->stopSection(); ?>
  20. <?php echo $__env->make('layouts.layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
  21. <?php /* /Users/fiervega/php_projects/book-property-management/resources/views/book/isbn_result.blade.php */ ?>

ビュー表示の時にエラーが出ているようです。

$answer['result']['title']の表示で引っかかっているようです。

 

この値に何が入っていたか…というと、API検索結果から書籍タイトルを出力しようとしています。

今回のテストではAPI検索した結果、書籍データがなかった場合です。データがない物を表示しようとしてもエラーになるはずです。

 

ということは、結果表示方法に問題があります。

書籍情報がない状態なのでタイトル情報は表示できません。にもかかわらず、プログラム上では表示させようとしています。

この処理を実施している場所で修正する必要があります。

 

問題のある処理を特定する

エラーとなっていたビュー内の処理を実際にコードをみて確認します。

# ~/resources/views/book/isbn_result.blade.php

@if (isset($answer['result']))
 <div class="isbn-result__box--bottom">
  <div class="isbn-result__box--image">
  @if (isset($answer['result']['cover']) == null)
   <img src="../image/no-entry.jpg">
  @else
   <img src="{{$answer['result']['cover']}}">
  @endif
  </div>
  <div class="isbn-result__box--title">{{$answer['result']['title']}}</div>
 </div>
@endif

$answer変数にresultが入っている場合に情報を取得するとしていました。

 

では、$answerは何だったのか?コントローラーを確認します。

# ~/app/Http/Controllers/BookController.php

// ISBNコードから本情報を取得
// 検索該当レコードのみデータ取得を実施
for ($i = 0; $i < $count; $i++){
 if ($isbnrecords[$i]['process'] == 'processing'){ // 処理変数が処理中の場合は処理を実施する
  $isbn = $isbnrecords[$i]['isbn'];
  $response = file_get_contents($isbn_url.$isbn);
  $result = json_decode($response, true);
  data_set($isbnrecords[$i], 'result', $result); // 結果を格納
  // ISBNレコード結果を確認
  // result[0]の情報有り無しで判定
  if($isbnrecords[$i]['result'][0] == null) {
   data_set($isbnrecords[$i], 'msg', '該当するISBNコードは見つかりませんでした。');
   // 未登録時はメッセージ格納
   data_set($isbnrecords[$i], 'process', 'error'); // 処理ステータスエラー
  }
 }
}
 
for ($i = 0; $i < $count; $i++){
 if ($isbnrecords[$i]['process'] == 'processing'){ // 処理変数が処理中の場合は処理を実施する
 // ISBNレコードがあれば追加処理
 $savedata = new Bookdata;
 // summaryData取得
 $getdata = $isbnrecords[$i]['result'][0]["summary"];
 
 〜 中略 〜
 
}
 
// ビューに出力
return view('book.isbn_result',['answers' => $isbnrecords]);
}

APIによる検索処理以降の抜粋です。

$answerは最後のビュー出力で登場します。元々コントローラー上では$isbnrecordsとして複数のISBNデータを扱っていました、ビュー表示の際に$answersとして引き渡しています。

そして、ビュー側で$answersから$answerとして1件ずつ取り出して処理しています。

 

ビュー表示のエラーとなっていた処理は$answer['result']['title']でした。

つまりresultというキーに対してのバリューがあれば表示しています。

このデータはAPI検索結果をそのまま追加しているので、仮にタイトル情報がなかったとしても追加されているので、resultというキーが存在する状態となっていました。

resutlというキーがあれば、ビュー画面上で取り出す処理が実施されてしまいます。これが原因のようです。

 

処理を修正する

修正手順を考える

resultというキーに対してデータを追加している理由は何だったか?というと、結果表示画面でユーザーが新規登録を希望したISBN登録結果としてどんなタイトルの書籍が追加されたかを表示するためです。

しかし、API検索された場合に必ず配列に検索結果を入れていたことにより問題が発生していました。

では、どうするか?

APIデータ取得後の処理順序をみなおすことで解決できそうです。

 

現状

ISBNデータ取得→配列追加→情報判定→メッセージ追加

修正

ISBNデータ取得→情報判定→配列追加→メッセージ追加

 

今までは、取得情報をresultというキーで配列内に追加しています。その後にタイトル有無判定等を実施していました。

しかし、先に受け取った情報を評価し、タイトルがなければ初めから配列にいれなければ今回のエラーは回避できます。 

そして、初めから配列に入れないことで配列内に不要なリソースを追加することも防げます。これは将来件数を大量追加した際などに大きな差となります。

 

処理を修正する 

 

修正ポイントが分かったので、実際に修正しました。

 

修正前

# ~/app/Http/Controllers/BookController.php

// ISBNコードから本情報を取得
// 検索該当レコードのみデータ取得を実施
for ($i = 0; $i < $count; $i++){
 if ($isbnrecords[$i]['process'] == 'processing'){ // 処理変数が処理中の場合は処理を実施する
  $isbn = $isbnrecords[$i]['isbn'];
  $response = file_get_contents($isbn_url.$isbn);
  $result = json_decode($response, true);
  data_set($isbnrecords[$i], 'result', $result); // 結果を格納
  // ISBNレコード結果を確認
  // result[0]の情報有り無しで判定
  if($isbnrecords[$i]['result'][0] == null) {
   data_set($isbnrecords[$i], 'msg', '該当するISBNコードは見つかりませんでした。');
   // 未登録時はメッセージ格納
   data_set($isbnrecords[$i], 'process', 'error'); // 処理ステータスエラー
  }
 }
}

修正後

# ~/app/Http/Controllers/BookController.php

// ISBNコードから本情報を取得
// 検索該当レコードのみデータ取得を実施
for ($i = 0; $i < $count; $i++){
 if ($isbnrecords[$i]['process'] == 'processing'){ // 処理変数が処理中の場合は処理を実施する
  $isbn = $isbnrecords[$i]['isbn'];
  $response = file_get_contents($isbn_url.$isbn);
  $result = json_decode($response, true);
  // ISBNレコード結果を確認
  // result[0]の情報有り無しで判定
  if($result[0] == null) {
   data_set($isbnrecords[$i], 'msg', '該当するISBNコードは見つかりませんでした。');
   // 未登録時はメッセージ格納
   data_set($isbnrecords[$i], 'process', 'error'); // 処理ステータスエラー
  } else {
   data_set($isbnrecords[$i], 'result', $result); // 結果を格納
  }
 }
}

API結果を配列に格納する場所を変更しました。

先に書籍情報があるか判定を実施し、ない場合はメッセージ処理と処理ステータスの変更を実施。else時、つまり書籍情報がある場合と分岐させて配列に追加することにしました。

こうすることで、情報があった場合のみ配列に追加されることとなります。

この状態で前回作成したテストを再度実施し、問題なく動作することが確認できました。

 

f:id:Fippiy:20190518162031p:plain

処理結果表示

実際にISBN登録を実施して、想定していた表示になっていることを確認しました。
 

 

さらに手直しを行う 

ビュー表示処理を見直す

エラーは解消されましたが、ここで改めてビュー表示の処理を確認してふと思いました。

@if (isset($answer['result']))

resultにデータがある場合に対して処理をしていました。API結果がある場合のみこの情報があり、書籍情報を出力しています。

しかし、この処理をそのまま読むと「配列要素にresultがある場合に書籍情報を表示する」となっています。

ISBN情報取得が正常終了していれば「正常に処理が完了しているデータに対して取得情報を表示する」と言う方が分かりやすいです。

 

「配列要素にresultがある場合に書籍情報を表示する」

「正常に処理が完了しているデータに対して取得情報を表示する」

どちらがより分かりやすいか…といえば後者でしょう。

 

ここで、前回記事にて処理を見直した時に追加した「処理ステータス」を利用することにしました。

配列内に処理中を示すキー$processを作成し、処理中・エラー・処理完了と要素毎にステータスを付与していました。

正常完了しているものだけをビュー画面で取得するという処理にすれば、より分かりやすくなり、正しくデータが取得できているものだけを選択することができます。

 

コードを修正する

 

修正前

@if (isset($answer['result']))
 <div class="isbn-result__box--bottom">
  <div class="isbn-result__box--image">
  @if (isset($answer['result']['cover']) == null)
   <img src="../image/no-entry.jpg">
  @else
   <img src="{{$answer['result']['cover']}}">
  @endif
  </div>
  <div class="isbn-result__box--title">{{$answer['result']['title']}}</div>
 </div>
@endif

修正後

@if ($answer['process'] == 'completion')
 <div class="isbn-result__box--bottom">
  <div class="isbn-result__box--image">
  @if (isset($answer['result']['cover']) == null)
   <img src="../image/no-entry.jpg">
  @else
   <img src="{{$answer['result']['cover']}}">
  @endif
  </div>
  <div class="isbn-result__box--title">{{$answer['result']['title']}}</div>
 </div>
@endif

書籍情報を出力しているコードです。

元々isset($answer['result']として、resultキーがある場合を指定していましが、正常に処理が完了しているパターンのみ書籍情報が格納されているので$answer['process'] == 'completion'として、処理が完了している物に対して書籍情報を出力としました。

処理を書き換えた上で、フォームからの入力チェックとテストを実施して問題ないことを確認し、想定しうる問題は解決できました。