Help us understand the problem. What is going on with this article?

AngularJSのProtractorでngMockE2Eの$httpBackentを使ってテストを書く際に気をつけること

More than 5 years have passed since last update.

まだ理解が浅い模様。

注意というかexpectとwhenの違いの話。

前提

  • AngularJSにはngMockE2EというE2E用のモックが用意されており、こちらを使用することにより、サーバを用意しなくてもモックでテストが書ける。

ngMockE2Eの\$httpBackendにはngMockの\$httpBackendと違って、expect系はなくwhen系のメソッドしかない

問題

よくある入力フォームのE2EテストをAPIの呼び出し部分にモックを使って書いていて、正常な登録と、エラー表示をまとめてテストしようとして、うまくいかなかった。

sign_in_spec.coffee
"use strict"

describe "ユーザ登録画面", ->
  beforeEach ->
    browser.addMockModule("httpBackendMock", ->
      angular.module("httpBackendMock", ["ngMockE2E"])
      .run(($httpBackend) ->
        $httpBackend.whenPOST("/api/sign_in",
          {
            user:
              email: "test@example.com"
              password: "password"
                password_confirmation: "password"
          }
        ).respond(201, {
            "user":
              "id": 480
          })
        $httpBackend.whenPOST("/api/sign_in",
          {
            user:
              email: "test@example.com"
              password: "password"
              password_confirmation: "12345678"
          }
        ).respond(422, {
            "status": 422,
            "messages": [
              "パスワード再確認とパスワードの入力が一致しません。"
            ]
          })
        $httpBackend.when("GET", /.*/).passThrough()
      )
    )

  beforeEach ->
    browser.get("/sign_in")

  it "エラー確認、登録後、別画面に遷移", ->
    email = element(By.model("registration.email"))
    password = element(By.model("registration.password"))
    password_confirmation = element(By.model("registration.password_confirmation"))
    button = element(By.tagName("button"))
    email.sendKeys("test")
    error = element(By.css(".form-group span:not([class=ng-hide])")).getText()
    expect(error).toBe "メールアドレスを入力してください"
    email.clear()
    error = element(By.css(".form-group span:not([class=ng-hide])")).getText()
    expect(error).toBe "メールアドレスは必須です"
    email.sendKeys("test@example.com")
    # .....省略.......
    password_confirmation.sendKeys("12345678")
    # この時点では、passwordとpassword_confirmationが違う事により失敗のメッセージのレスポンスを期待する
    button.click()
    expect(errorElement.getText())toBe "パスワード再確認とパスワードの入力が一致しません。"
    # .....省略.......
    # 正常系
    button.click()
    expect(browser.getLocationAbsUrl()).toMatch "#/other_page"


api/sign_inへのPOSTのバックエンド定義を2つ書いている。

ユーザパラメータの違いにより、使われるモックが変わることを期待しているが、片方しか呼ばれない。

これは考えてみれば当たり前で、whenはexpectと違って、定義されたリクエストが行われた場合に用意されたレスポンスを返し、リクエストを厳密にアサートしないバックエンドを定義するものだから。
パラメータやヘッダーなど、このようにリクエストしてほしいというアサーションを期待するのはexpect系を使わないと書けない。

ちなみに $httpBackend.when("GET", /.*/).passThrough()で、モック以外の通信(この場合、静的なHTMLの取得)を全てサーバと通信するようにしている。

解決

ngMockE2Eの$httpBackendにはexpectがないので、
正常系と失敗系でdescribeを分けて、それぞれにmockを定義してテストを行った。

sign_in_spec.coffee
"use strict"

describe "ユーザ登録画面", ->
  beforeEach ->
    browser.addMockModule("httpBackendMock", ->
      angular.module("httpBackendMock", ["ngMockE2E"])
      .run(($httpBackend) ->
        $httpBackend.whenPOST("/api/sign_in",
          {
            user:
              email: "test@example.com"
              password: "password"
              password_confirmation: "password"
          }
        ).respond(201, {
            "user":
              "id": 480
          })
        $httpBackend.when("GET", /.*/).passThrough()
      )
    )

  beforeEach ->
    browser.get("/sign_up")

  it "登録後、別画面に遷移", ->
    # test code here

describe "ユーザ登録画面error", ->
  beforeEach ->
    browser.addMockModule("httpBackendMock", ->
      angular.module("httpBackendMock", ["ngMockE2E"])
      .run(($httpBackend) ->
        $httpBackend.whenPOST("/api/sign_in",
          {
            user:
              email: "test@example.com"
              password: "password"
              password_confirmation: "12345678"
          }
        ).respond(422, {
            "status": 422,
            "messages": [
              "パスワードを再入力とパスワードの入力が一致しません。"
            ]
          })

        $httpBackend.when("GET", /.*/).passThrough()
      )
    )

  beforeEach ->
    browser.get("/sign_up")

  it "エラー表示確認", ->
   # test code here    

expect系が用意されていないのは、統合テストという性格上、実際にテスト用のAPIサーバを用意して実際の挙動でテストするのを推奨していて、モックは限定的に使えという意味合いなのかなと思った。

今回、APIサーバはRailsだが、RailsにAngularJSを載せるのではなく、AngularJSとRailsの役割を完全に切り離して開発しているので、E2Eテストでどうしようかなと模索中。
実際にrails serverをテスト環境で動かしてproxyでつなぐのがいいかなと思いつつ、DBデータのテスト事の処理をどうしようかと。
なにかいい方法を知っている方がいたら教えていただきたい感じ。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした