-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
(android) Add unit tests for run and retryPromise #445
Conversation
be5b50b
to
e298c60
Compare
It looks like the tests fail on Appveyor because I am using ES2015 features and that the Appveyor set up includes Node 4, which doesn't support these features. The tests pass on Node 6 and 8. I suggest removing Node 4 from there and adding 10. Would that be OK? |
Yes, node 4 is no longer supported so we're slowly going through all the repos and dropping it from CI while adding node 10. I think that's what we should do here, but please do it as its own pull request to avoid this one getting cluttered with unrelated changes :) |
e298c60
to
cbb9d8c
Compare
Codecov Report
@@ Coverage Diff @@
## master #445 +/- ##
==========================================
+ Coverage 44.27% 47.63% +3.36%
==========================================
Files 17 17
Lines 1694 1694
Branches 311 311
==========================================
+ Hits 750 807 +57
+ Misses 944 887 -57
Continue to review full report at Codecov.
|
Great PR 👍 There is one issue though: the handling of promises in tests. Right now, failing tests would time out and you wouldn't get to see the error that caused the rejection. To fix this, all async tests should return a promise instead of using the done callback. That way, Jasmine automatically handles resolution and rejection of these promises. Thanks a lot for the contribution! |
Thanks for the suggestion, I didn't realise that was possible. According to the Jasmine async docs, support for returning promises was added in 2.7, but cordova-android uses 2.6. I'll look into upgrading and see if it causes any issues. |
7847555
to
8167646
Compare
@raphinesse I updated Jasmine to the latest version (3.1.0) without any issues. I know it is suggested to squash commits in this repo, but I put the updating of this version in a second commit as I think it makes sense to keep it separate. As for the tests themselves, I have rewritten them to return the promises instead of calling done. There are some asynchronous tests where calling done is needed (such as testing that the promise is rejected). For those tests, I have added handling for the failure cases to ensure that all the tests fail in a meaningful way (that is, avoiding timeouts where possible). |
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've done a more thorough review. I also included fixes for all my comments in an additional commit. It looks like a whole lot of changes, but most of them are simple Q -> Promise transformations. Please see my review comments for explanations to my changes.
Thanks again for your contribution and please do let me know if you're comfortable with the changes I made. I'm looking forward to your forthcoming test PRs 👍
spec/unit/retry.spec.js
Outdated
.then(() => { | ||
done.fail('Unexpectedly resolved'); | ||
}) | ||
.fail(() => { |
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.
At least @dpogue and me have a dream of someday getting rid of Q. Unfortunately, using Q instances is often necessary because old code relies on Q-specific methods like fail
. Please use the methods provided by the native Promise
interface whenever possible. In this case use catch
instead of fail
.
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.
Ah I see! I specifically used Q rather than native Promises since I saw it being used everywhere else. I will use Promises from now on.
spec/unit/retry.spec.js
Outdated
|
||
retry.retryPromise(attempts, promiseFn) | ||
.then(() => { | ||
done.fail('Unexpectedly resolved'); |
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.
You can actually use Jasmine's global fail
and return retry.retryPromise(...)
to get rid of done
here too.
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 didn't realise Jasmine had a global fail
, that is very convenient 👍
spec/unit/retry.spec.js
Outdated
|
||
beforeEach(() => { | ||
deferred = Q.defer(); | ||
promiseFn = jasmine.createSpy().and.returnValue(deferred.promise); |
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.
We could make this Q-less too by doing the following instead:
const promise = new Promise((resolve, reject) => {
deferred = { resolve, reject };
});
promiseFn = jasmine.createSpy().and.returnValue(promise);
spec/unit/retry.spec.js
Outdated
|
||
for (let i = 0; i < attempts + 1; i++) { | ||
deferred.reject(); | ||
} |
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.
Looping is unnecessary and misleading here. promiseFn
will return the same rejected promise every time, so you can reject your deferred once at the top of the test.
spec/unit/retry.spec.js
Outdated
for (let i = 0; i < attempts + 1; i++) { | ||
deferred.reject(); | ||
} | ||
}); |
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.
All in all, this would give the following test:
const attempts = 3;
deferred.reject();
return retry.retryPromise(attempts, promiseFn).then(
() => fail('Unexpectedly resolved'),
() => expect(promiseFn).toHaveBeenCalledTimes(attempts)
);
spec/unit/run.spec.js
Outdated
|
||
run.help(); | ||
|
||
expect(message.indexOf('Usage:') > -1); |
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.
You are not actually testing anything here. Should be:
expect(message).toMatch(/^Usage:/);
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.
Whoops, that's embarrassing!
spec/unit/run.spec.js
Outdated
it('should print out usage and help', () => { | ||
let message = ''; | ||
spyOn(console, 'log').and.callFake(msg => { message += msg; }); | ||
spyOn(process, 'exit'); |
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 clobbers the global console.log
and process.exit
for the whole process. I'm not too comfortable with that. Could cause very obscure errors for other tests.
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 was worried about that when writing this, and looked into how to unspy a function in Jasmine, but it seems that Jasmine automatically unspies after a test is run. So, console.log
and process.exit
would not be spied in other functions. Personally I would prefer to explicitly unspy to avoid this confusion, but from what I can find, there is no way to do that.
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.
Interesting, I did not know that spies get unspied. OTOH it's a good thing, but OTOH I agree that explicit operations are preferable at times. Conveys intent and informs unsuspecting users like me 😅
Another note: when we finally have unbundled dependencies, we probably should replace So next time when you come across an untested homemade util, before investing time to write tests for it, consider looking for a lib that does the same job and include it instead. Having to maintain less code is always preferable. 😇 |
781bbba
to
98de909
Compare
Thanks for the detailed review, I really appreciate it. I would have looked into replacing I have replied to most of your comments. On the topic of spying on I am happy with your changes, and again very much appreciate your input. I will work on writing some more tests in another branch during the week. By the way, I rebased the branch with master as there was a conflict in |
Understandably so 😅 Given the temporary nature of global spies, please feel free to revert back to using them if you want. My bad for not knowing better. For me, this is good to go. I'd probably squash all commits, but I don't really care either way here. @dpogue what do you say? Thanks again for this great PR and any following ones 👍 |
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.
👍
d2f8d64
to
69e5481
Compare
Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
69e5481
to
8e9078d
Compare
Platforms affected
Android
What does this PR do?
I noticed the test coverage for
cordova-android
was rather low, so I decided to start writing some tests. This PR increases coverage from 44.69% to 47.83%. I first coveredretry.js
, which is a simple module that retries a promise-based function a given number of times. I also fixed a typo in this file which was mildly annoying.The main tests I have written are in
run.js
, which now has 100% test coverage (up from 27%). This module handles the selection of the device or emulator to run on.I would like to continue to add unit tests to help improve the code coverage, but am submitting this PR first to check that I am on the right track. I have used ES2015 notation for the tests to keep it more readable - I don't anticipate this to be a problem since it is supported in all recent node versions.
What testing has been done on this change?
I have run the tests using
npm run unit-tests
, and checked the coverage withnpm run cover
.Checklist