-
Notifications
You must be signed in to change notification settings - Fork 407
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
Add test coverage for lib/table.js
#178
Conversation
- Is there a better way to rest the 'handler override' than switching to beforeEach/afterEach?
Taking a look now! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, Rebecca! I especially appreciate the effort you've made to use the existing infrastructure while preserving the readability of your tests. (I can't find any online resources describing the "mystery guest" problem, though...)
Anyway, I've made a few specific suggestions. Here are some ideas for a few more tests:
forEach
- no results are foundforEach
- an error is thrown in the first callbackforEach
- an error is thrown in the second callbackeachPage
- when there are multiple pages
As a general extension to the HTTP error tests: could you verify that each statusCode
you choose is being defined on the error object? That seems important for the developers who are debugging problems.
test/create.test.js
Outdated
}, | ||
{typecast: true} | ||
) | ||
.catch(function(err) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test will time out if the promise returned by the library is fulfilled. To make reporting a little faster and more descriptive, we could do something like...
- .catch(function(err) {
+ .then(function() {
+ throw new Error('Promise fulfilled unexpectedly');
+ }, function(err) {
expect(err).not.toBeNull();
- done();
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems useful for future debugging. Done.
res.json({ | ||
id: req.params.recordId, | ||
fields: {Name: 'Rebecca'}, | ||
createdTime: '2020-04-20T16:20:00.000Z', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is createdTime
? It seemed like something that we should test, but there doesn't appear to be any reference to it in the application code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is returned by the API so I thought I would include it to make the tests more realistic, but there aren't any methods provided by the library to access it. (Except through the Record#_rawJson
which is clearly meant to be private.
I could see an argument to removing it because maybe it obscures the test more by providing extraneous information. But I also see an argument of leaving it in because I imagine it might be exposed in the future and it provides a realistic understanding of the format of the json returned from the API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend toward the "extraneous information" interpretation, but I'm happy to go with your preference here, particularly because you're following precedence.
|
||
testExpressApp.set('handler override', function(req, res) { | ||
expect(req.method).toBe('GET'); | ||
expect(req.url).toBe('/v0/app123/Table/record1?'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(The trailing ?
is a little fragile. Ideally the library shouldn't add it, but it doesn't matter if it's there or not. There are ways we could address that, but we wouldn't need them if the library didn't do this. Probably best to accept this fragility and plan to change the library later.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be fine switching to toMatch
https://jestjs.io/docs/en/expect.html#tomatchregexporstring
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm switch to match seem unideal too. I'm just going to leave it as is for now.
test/list.test.js
Outdated
{opts: 'arepassedalong'}, | ||
function(record) { | ||
expect(record.getId()).toBe('recordA'); | ||
expect(record.get('Name')).toBe('Rebecca'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test will pass if the callback is never invoked. Could you increment a counter in this function and then verify its value in the second callback?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Done.
test/list.test.js
Outdated
fields: {Name: 'Rebecca'}, | ||
createdTime: '2020-04-20T16:20:00.000Z', | ||
}, | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding a second record so we can verify that the library is truly iterating?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Done.
test/list.test.js
Outdated
.base('app123') | ||
.table('Table') | ||
.forEach( | ||
{opts: 'arepassedalong'}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit distracting for this particular test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. Removed!
.table('Table') | ||
.select() | ||
.eachPage(function page(records) { | ||
records.forEach(function(record) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will also pass for any number of records. Could you verify records.length
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
airtable | ||
.base('app123') | ||
.table('Table') | ||
.select({maxRecords: 'should not be a string'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will pass if the calls to base
or table
throw an error. Unlikely, sure, but still worth taking those parts of the chain outside of the assertion function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added matching of the specific error messages. I think this doesn't exactly address you point. But it does make it more clear that the errors I'm seen being throw are the ones I'm trying to throw. Is this a good enough way to address the issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that works for me
Thanks for all the feedback.
Btw, I think this is the original source of the term: http://xunitpatterns.com/Obscure%20Test.html#Mystery%20Guest |
That makes sense. Done. |
- Add tests with more than one record - Add test with no records - Add iteration counter to ensure all expectations are run
I think it makes sense to skip the |
To test that errors are thrown in tests. This way if tests unexpectly fufill the promise they immediately error instead of just timeing out.
I'm skipping adding these tests because the code doesn't handle a error be thrown in the callback at all it just bubbled up to the global event handler. I might want to change the behavior so that such an error is handled, but this is for an already deprecated function, so I don't think it makes sense to address that behavior change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondrous!
Please note that in this context, my approval carries even less weight than it normally does. We'll need someone at Airtable to merge, perhaps @kasrak.
@kasrak - A lot of the test changes are interconnected, so for record tests I ended up branching of this branch. I have another branch ready for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Tables
's interactions withRecord
andQuery
)select
,update
, andcreate
methods were already pretty extensively tested using hard coded return values in the mock API express app. I added tests for error cases using the existing "handler override" functionality. Since the "handler override" is only resets ontearDownAsync
all cleanup and setup needs to be down in these test files oneach
test. This probably makes the test slower. I could instead add some mechanism to reset thehandler
after each run.