LoginSignup
3
1

More than 3 years have passed since last update.

E2Eテスト(protractor)で行ったテストをエクセルにまとめるVBAを組んだ

Posted at

そろそろスクショ職人から卒業すべく、protractorを使ってAngularの自動テスト技術を磨いています。

でも上司への提出用に、エクセルに証跡を残していかないといけないんだろーなー。
自動で取得したスクショをエクセルに貼って、テストの流れがわかるように矢印図形なんかもつけて…
ああ、嫌になりますね。やっぱりスクショ職人じゃないか!
こんな状態になる前に、テストコードとスクショファイルから自動で証跡を作ってくれるVBAを組みました。

テスト対象のAngularコード

app.component.html
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>

<input type="button" id="click1" value="クリック!" routerLink="./sub" />
<input type="button" id="click2" value="もどる" routerLink="./.." />
<br />
<router-outlet></router-outlet>
sub.component.html
<h1>Angularです</h1>
<img src="../../assets/react.png" />
app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SubComponent } from './sub/sub.component';


const routes: Routes = [
  { path: 'sub', component: SubComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

大体こんな感じの超簡易なコードです。app.component.tsとかは省略。
これを動かすとこんな感じ。

1.png
クリックボタンを押します。
2.png
下にsubのコンポーネントが現れました。
このsubコンポーネントの「Angularです」の文字列が間違いなく表示されているかチェックすることを今回のテストの目的とします。
現実的じゃないけど許して

テストコード

以下がテストコード。
app.po.tsは置いてきた。

app-e2e-spec.ts
import { browser, logging, by, element } from 'protractor';

describe('workspace-project App', () => {
  const fs = require('fs');

  beforeEach(() => {
    browser.get('');
  });

  it('テストケース1', () => {
    browser.takeScreenshot().then((png) => {
      saveScreenshot(png, 'スクショ1.png');
    })

    element(by.id('click1')).click();

    expect(element(by.css('app-sub h1')).getText()).toEqual('Angularです');

    browser.takeScreenshot().then((png) => {
      saveScreenshot(png, 'スクショ2.png');
    })
  });

  afterEach(async () => {
    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
    expect(logs).not.toContain(jasmine.objectContaining({
      level: logging.Level.SEVERE,
    } as logging.Entry));
  });

  //スクショ保存
  function saveScreenshot(data: any, name: string) {
    const stream = fs.createWriteStream(name);
    stream.write(new Buffer(data, 'base64'));
    stream.end();
  }
});

itでテストケースのひとかたまりです。
そこで起動直後のスクショを「スクショ1.png」という名前で取得し、
その後クリックボタンを押下。
遷移したsubコンポーネントのh1テキストが「Angularです」だということを確認しています。
そしてまたスクショ保存。「スクショ2.png」という名前。

VBAコード

これでテストは終わりですが、ここからが今回のキモ。
今回私が組んだVBAはこちら。


Type TestStruct
    testCaseIt As String
    testCaseSc() As String
End Type

Sub subLfFileRead()

    Dim buf As String
    Dim arrOneLine() As String
    Dim i As Long
    Dim RE, strPattern As String, reMatch
    Dim RESc, strPatternSc As String, reMatchSc
    Dim testSet() As TestStruct, intTestSet As Integer: intTestSet = 0
    Dim intTestCaseScCnt As Integer: intTestCaseScCnt = 0
    Dim strPath As String: strPath = Cells(4, 1).Value

    Set RE = CreateObject("VBScript.RegExp")
    Set RESc = CreateObject("VBScript.RegExp")

    strPattern = "it\(\'.+\'"
    strPatternSc = "savescreenshot.+\'.+\'"

    RE.Pattern = strPattern
    RE.IgnoreCase = True
    RE.Global = False

    RESc.Pattern = strPatternSc
    RESc.IgnoreCase = True
    RESc.Global = False

    'テストコードオープン
    With CreateObject("ADODB.Stream")
        .Charset = "UTF-8"
        .Open
        .LoadFromFile Cells(2, 1).Value
        buf = .ReadText
        .Close
        arrOneLine = Split(buf, vbLf)
    End With

    'テストコード読み込み
    For i = 0 To UBound(arrOneLine) - 1
        'コメント行は除外
        If InStr(arrOneLine(i), "//") = 0 Then
            '正規表現の検索・テストケース
            Set reMatch = RE.Execute(arrOneLine(i))
            If reMatch.Count > 0 Then
                ReDim Preserve testSet(intTestSet)
                testSet(intTestSet).testCaseIt = fncReturnCommaExclude(reMatch(0))
                intTestSet = intTestSet + 1
            End If

            '正規表現の検索・スクショ名
            Set reMatchSc = RESc.Execute(arrOneLine(i))
            If reMatchSc.Count > 0 Then
                ReDim Preserve testSet(intTestSet - 1).testCaseSc(intTestCaseScCnt)
                testSet(intTestSet - 1).testCaseSc(intTestCaseScCnt) = fncReturnCommaExclude(reMatchSc(0))
                intTestCaseScCnt = intTestCaseScCnt + 1
            End If
        End If
    Next i

    Call subFilePasteSheet(testSet, strPath)


End Sub

Sub subFilePasteSheet(ByRef structType() As TestStruct, ByVal strScreenshotPath As String)

    Dim myFileName As String
    Dim sglLastPictureHeight As Single
    Dim sglLastPictureWidth As Single
    Dim sglAllowHeight As Single
    Dim myShape As Shape
    Dim newSheet As Worksheet

    sglLastPictureHeight = 0
    sglAllowHeight = 0
    sglLastPictureWidth = 0

    For i = 0 To UBound(structType)
        Set newSheet = ThisWorkbook.Worksheets.Add
        newSheet.Name = structType(i).testCaseIt
        newSheet.Cells(i + 1, 1).Value = structType(i).testCaseIt

        For j = 0 To UBound(structType(i).testCaseSc())
            myFileName = strScreenshotPath & "\" & structType(i).testCaseSc(j)

            Set myShape = newSheet.Shapes.AddPicture( _
                  Filename:=myFileName, _
                  LinkToFile:=True, _
                  SaveWithDocument:=False, _
                  Left:=54, _
                  Top:=13.5 + sglLastPictureHeight * j + sglAllowHeight * j, _
                  Width:=0, _
                  Height:=0)

            With myShape
                .ScaleHeight 1, msoTrue
                .ScaleWidth 1, msoTrue
            End With

            sglLastPictureHeight = myShape.Height
            sglLastPictureWidth = myShape.Width

            If j <> UBound(structType(i).testCaseSc()) Then

                Set myShape = newSheet.Shapes.AddShape( _
                                    Type:=msoShapeDownArrow, _
                                    Left:=(54 + sglLastPictureWidth) / 2 - 100, _
                                    Top:=13.5 + sglLastPictureHeight * (j + 1) + sglAllowHeight * j, _
                                    Width:=200, _
                                    Height:=45)
                sglAllowHeight = myShape.Height
            Else
                sglAllowHeight = 0
            End If

        Next j

        sglAllowHeight = 13.5
    Next i

End Sub

'コンマに挟まれている文字列を返す関数
Function fncReturnCommaExclude(ByVal strData As String)

    fncReturnCommaExclude = ""

    intfrontcomma = InStr(strData, "'") + 1
    intBackComma = InStr(intfrontcomma, strData, "'")

    fncReturnCommaExclude = Mid(strData, intfrontcomma, intBackComma - intfrontcomma)

End Function


ざっくり言うと、テストコード内のitやsavescreenshotを検索、その中から文字列抽出、
抽出した文字列からスクショを拾いエクセルのシートに貼る。スクショのサイズも見ながら調節する。

こんな感じです。
これをこんな感じで使う。
3.png
vscodeの改行コードがLfになってたので、そっちのみ対応してます。CrLfまでやるのは気が向かなかった。

実行ボタンを押すと新しくこんなシートができあがります。(倍率を低くする処理はありません。)
4.png

よく見るエビデンスの出来上がりですね。
これでスクショ職人は卒業だ!
でも今回の記事に載せる画像作るためにいくつかスクショ撮ったな…
やっぱりスクショ職人じゃないか!

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1