Jonathan Tuzman
Jonathan Tuzman

Reputation: 13262

Generator not catching error from API call

I'm working with an API that doesn't return anything "error-like" for an incorrect login (just a normal object with a code -- incorrect_password or invalid_username -- an HTML message), so in my API calling function I'm turning the code into an error:

export async function loginWithApi(creds) {
  try {
    const res = await axios.get(ApiUrls.login, { params: creds });
    if (res.data.data) {
      return res.data;
    } else if (res.data.code) {
      throw Error(res.data.code.titleize());
    } // else, it should be an actual error
  } catch (err) {
    return err;
  }
}

This works correctly, throws correctly, returns an Error when it should.

However, when testing my saga generator action that calls this function, the error isn't being caught, it's going unabated through the try block:

function

export function* loginSaga({ username, password }) {
  const creds = { username, password };
  try {
    const res = yield call(loginWithApi, creds);
    console.log(res);
    yield put({ type: "LOGIN_SUCCESS", user: res.data });
  } catch (error) {
    yield put({ type: "LOGIN_FAILURE", error });
  }
}

tests

 describe("login action", () => {
    let gen;
    afterEach(() => {
      expect(gen.next().done).toBe(true);
    });

    it("should return a user object on a successful login", () => {
      gen = loginSaga(success.creds);
      gen.next(); // call api
      expect(gen.next(mockResponse.success).value).toEqual(
        put({ type: "LOGIN_SUCCESS", user: mockResponse.success.data })
      );
    });

    it("should return an error when passed an invalid username", () => {
      gen = loginSaga(badUser.creds);
      gen.next(); // call api
      // mockReponse.usernameError: Error("Invalid Username")
      expect(gen.next(mockResponse.usernameError).value).toEqual(
        put({ type: "LOGIN_FAILURE", error: "Invalid Username" })
      );
    });

    it("should return an error when passed an invalid password", () => {
      gen = loginSaga(badPw.creds);
      gen.next(); // call api
      // mockResponse.passwordError: Error("Incorrect Password"),
      expect(gen.next(mockResponse.passwordError).value).toEqual(
        put({ type: "LOGIN_FAILURE", error: "Incorrect Password" })
      );
    });
  });

test output (excerpts)

  ● Saga Actions › login action › should return an error when passed an invalid username

    expect(received).toEqual(expected) // deep equality

    - Expected
    + Received

      Object {
        "@@redux-saga/IO": true,
        "combinator": false,
        "payload": Object {
          "action": Object {
    -       "error": "Invalid Username",
    -       "type": "LOGIN_FAILURE",
    +       "type": "LOGIN_SUCCESS",
    +       "user": undefined,
          },
          "channel": undefined,
        },
        "type": "PUT",

● Saga Actions › login action › should return an error when passed an invalid password

    expect(received).toEqual(expected) // deep equality

    - Expected
    + Received

      Object {
        "@@redux-saga/IO": true,
        "combinator": false,
        "payload": Object {
          "action": Object {
    -       "error": "Incorrect Password",
    -       "type": "LOGIN_FAILURE",
    +       "type": "LOGIN_SUCCESS",
    +       "user": undefined,
          },
          "channel": undefined,
        },
        "type": "PUT",
      }

Logging confirms that the loginSaga function is receiving an Error in the second two tests. I can't quite determine why that error isn't being caught.

Upvotes: 0

Views: 57

Answers (1)

skyboyer
skyboyer

Reputation: 23685

Finally got it.

So you need make generator think that Exception happened for one of its yield. To make that we have generator.prototype.throw().

So next code should work as expected:

it("should return an error when passed an invalid username", () => {
  gen = loginSaga(badUser.creds);
  gen.next(); // call api
  // mockReponse.usernameError: Error("Invalid Username")
  expect(gen.throw(mockResponse.usernameError).value).toEqual(
    put({ type: "LOGIN_FAILURE", error: "Invalid Username" })
  );
});

Upvotes: 1

Related Questions