Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make (more) sure the error we get is the one we expect #267

Merged
merged 2 commits into from
Feb 14, 2017

Conversation

alextes
Copy link
Contributor

@alextes alextes commented Jan 23, 2017

Since we set the statusCode to 500 in the changed test, our code triggers a HTTPError. Regardless of the parsing conditional's outcome that may or may not throw. Because of this, the test would pass even if the code does not throw a ParseError. This change makes sure the error is the got.ParseError we expect. By no means perfect but good enough to save me and hopefully others from confusion.

It is a minor change, but it still tripped me up when working on #264. Hopefully, you too feel this would improve, not complicate the code 😄 .

Since we set the statusCode to 500 in the changed test, a HTTPError is
triggered whether the parsing throws or not. Because of this the test
would still pass without the parse option set when it shouldn't. This
change makes sure the error is the got.ParseError we expect.
@sindresorhus
Copy link
Owner

Always happy to improve the tests. Thanks for the PR. I think it would be better to switch the whole test to use t.throws() instead though:

test('should have statusCode in err', async t => {
	const err = await t.throws(got(`${s.url}/non200-invalid`, {json: true}), got.ParseError);
	t.is(err.statusCode, 500);
});

@alextes
Copy link
Contributor Author

alextes commented Jan 23, 2017

@sindresorhus hi Sindre!
Me too! Should've thought of that. I'll update this one and then have a look around for others that could be improved that way 😺 .

Ava actually has the perfect function for this. Let's use it!
@alextes
Copy link
Contributor Author

alextes commented Jan 23, 2017

I had no idea Ava's .throws could take an error to compare to, that's perfect!
Thanks for the suggestion.
If I understand ava's blowing up correctly we shouldn't wrap the main got function in an anonymous function because it's caught at some point and returned in the form of a rejected promise.

Sidenote:
breaking the test causes the following message:

json › should have statusCode in err
  Cannot read property 'host' of undefined`

That seems less than ideal. I'm not sure why that happens or how to improve it. I'll do some digging ^_^.

@sindresorhus
Copy link
Owner

If I understand ava's blowing up correctly we shouldn't wrap the main got function in an anonymous function because it's caught at some point and returned in the form of a rejected promise.

Yes, no need, since we're passing in a promise, which is handled by t.throws.

breaking the test causes the following message:

How are you breaking the test?

@alextes
Copy link
Contributor Author

alextes commented Jan 23, 2017

@sindresorhus

test('should have statusCode in err', async t => {
	const err = await t.throws(got(`${s.url}/non200-invalid`), got.ParseError);
	t.is(err.statusCode, 500);
});

I'm having a very hard time tracing the cause of the AssertionError .throws seems to throw.
Time to pull out node-inspector :p.

Also cool hat ٩(◕‿◕。)۶.

@sindresorhus
Copy link
Owner

@alextes
Copy link
Contributor Author

alextes commented Jan 23, 2017

@sindresorhus I know ava pretty well. She's amazing! Was already messing around with that but I couldn't seem to break T_T.

Regardless I almost got it. No need for you to spend time on this. Go be awesome!
If you're interested it's going wrong over at:

function stdError(error, opts) {
	if (error.code !== undefined) {
		this.code = error.code;
	}

	Object.assign(this, {
		message: error.message,
		host: opts.host,
		hostname: opts.hostname,
		method: opts.method,
		path: opts.path
	});
}

got.ParseError = createErrorClass('ParseError', function (e, statusCode, opts, data) {
	stdError.call(this, e, opts);
	this.statusCode = statusCode;
	this.statusMessage = http.STATUS_CODES[this.statusCode];
	this.message = `${e.message} in "${urlLib.format(opts)}": \n${data.slice(0, 77)}...`;
});

@alextes
Copy link
Contributor Author

alextes commented Jan 23, 2017

I finally understand @sindresorhus 😓 . The check is still valid. After adding some defaults a ParseError no longer blows up with access of undefined and ava correctly checks we are looking at an instance of a ParseError.

Now to the silly bit. The reported error by ava is influenced by the caught error. In the case of breaking the test not to throw the expected ParseError, a thrown HTTPError. Which in this case is an error that we don't want. I'm guessing when the code does not throw, we run into the HTTPError throw, ava notices they are unequal, and for a reason I don't understand uses the properties on the error that is not the one we're looking for when providing the user with feedback. In other words, when the test fails we end up with a very strange error message (in verbose mode):

  ✖ json › should have statusCode in err Response code 500 (Internal Server Error)

  1 test failed [23:32:50]


  1. json › should have statusCode in err
  AssertionError: Response code 500 (Internal Server Error)

Options:

  • Catching and rethrowing in the test to provide a clear error. Makes little sense.
  • We could go back to the code I originally PR'd. No .throws.
  • We could leave it like this. For the passing case, this is the cleanest way we could write things down I feel. It does go downhill fast when the test fails.. which is kinda what it should be built for. Ava also seems to prefer the received (incorrect / coincidentally thrown) error's message property to the message I can optionally pass so that doesn't help either.
  • Submit a PR to Ava! When .throws catches an error that is not judged valid by optional second arg we use the optional message from the third arg if there is any.

For the third option, I would also have to add a commit that sets defaults for the ParseError constructor since for a reason I'm not entirely sure about .throws ends up invoking the constructor passed to compare to, its a custom error constructor, and it needs at least an empty options object for one.

I have a bit of a headache, but it was a good lesson! Hope to keep contributing 😄 .

@sindresorhus
Copy link
Owner

Submit a PR to Ava! When .throws catches an error that is not judged valid by optional second arg we use the optional message from the third arg if there is any.

This ;) Could you open an issue (or PR) on AVA about this?

@sindresorhus
Copy link
Owner

Oh wow. Good debugging! :)

@sindresorhus sindresorhus merged commit 39bf828 into sindresorhus:master Feb 14, 2017
@sindresorhus
Copy link
Owner

Hope to keep contributing 😄 .

Please do! I'm in https://gitter.im/sindresorhus/meta if you have any questions. Sorry about the delay. I was busy in real life.

@alextes
Copy link
Contributor Author

alextes commented Feb 14, 2017

This ;) Could you open an issue (or PR) on AVA about this?

On it! I'll try and open a solid PR 😁.

Oh wow. Good debugging! :)

ty ty 😊

Please do! I'm in [...]

Awesome! See you there.

Sorry about the delay. I was busy in real life.

No problem of course. I get you're busy. You're still getting the cat gifs I promised over in NodeJS slack 😸.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants