-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Unit testing of Cloud Code functions #2488
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
Comments
Register the hooks pointing to a local version via ngrok and then invoke the tests. You can use the scripts here: https://github.com/ParsePlatform/CloudCode-Express |
I've been using https://github.com/HustleInc/parse-mockdb and it works well. I'm going to try @maysale01 suggestion though too. I'll report back on which I prefer and why (not for a couple of days though). |
I found this during my search for general info on unit testing Parse applications. My plan for cloud code is essentially to extract the functions out and unit test them with fake request / response objects. i.e. instead of main.js
split into two files doThing.js
main.js
Then
however the stumbling block I've met is not so much the "calling" of a function to test it, but the amount of things that require mocking / stubbing. Its the nature of using a BaaS or any third party service that large areas of functionality need to be mocked / stubbed, however i have to admire the approach of Angular, in providing sort of pre-done stubs / helpers for this. FWIW id personally not recommend the ngrok web hook approach for unit tests the amount of moving parts there seems a bit high to me personally. That approach seems more for E2E testing |
@chrismatheson I do more or less what you're describing using @TylerBrock's https://github.com/HustleInc/parse-mockdb Works like a charm. |
What are the benefits of using parse-mockdb instead of parse-server? The "real" server can be run as an npm module as well, so I don't really get why one should use an incomplete re-implementation of the API. In my current test setup, I start a (local) Parse server before running the tests and have my tests communicate with the real Cloud Code functions. The only downside I see is that for a really clean test run, I would have to reset the mongodb (which in my setup runs as a normal background process on the system). I find this both easier and more realistic than mocking various layers. But I would be interested in the opinion of others on that topic. |
i like that with the parse-mockdb i don't have to have a mongo service running. i've tinkered a little with running parse-server for unit tests and have been looking at the parse-server unit tests, but the setup is onerous to me so far. will try again at some point (soon probably) but in the meantime, the mock setup was reasonably easy to get going and has worked well. |
Seems extensively answered, closing |
I'm also looking for a tool to test my cloud-code, jobs (using agenda) as well as save/delete hooks. I started with python-nosetests mainly to test parse-server functionality at the earliest stages of the parse-server release. I'm not sure whether using nosetests in that context is the best idea, though, but it's the testing tool I'm most familiar with, and it started off as a very basic testcase. The nosetests make calls to the actual API - I have a test environment setup just for that - instead of using a mock database or API. In my opinion, that's the whole point of testing. Currently I'm looking into expanding my tests to achieve a higher coverage, but I'm not sure if I should stick to nosetests, or if there's a better way/tool to tackle this. |
You should test your jobs the same way you test any express endpoint. Cloud code supports
You can very well have:
Then you could use |
@flovilmart Good idea! Didn't think about that. Any ideas regarding the testing tool itself. Nosetests currently do the job, but I'd like to know, if there's a better way to test parse (or node in general) specific code. What do you use for testing parse-server? |
@majidhassan parse-server uses jasmine for unit tests. I've found that looking at how parse-server does unit tests to be useful in designing my own unit tests. |
I use gulp to run tests and mocha to write them. Mocha is great for Parse because it has good support for promises and testing async code (haven't used jasmine but it seems to be equally well suited); however, I think the test framework itself is the least interesting aspect of a Parse test setup. Here are some ideas which helped me to create a testing environment: I use a local Parse server and mongodb to avoid cluttering my public hosted services with test data. After overcoming the initial setup hurdle, this works like a charm. In my tests, I call CC functions via the Parse API ( I run both CC and tests in the same process: the Parse server is started as a "before test" action and stopped as an "after test" action. This allows me to debug (e.g. set breakpoints) the test code as well as the server-side code, which has helped me tremendously in finding bugs. The only drawbacks I have encountered with this setup is that a) it depends on a running mongodb instance, which I have to manually reset (haven't found a good way for automating that yet), and b) the mixed log output of test runner and server code can get a bit confusing. |
@majidhassan I haven't used code coverage with my Cloud Code tests so far, but just gave it a try with a tool called istanbul, which frankly I find awesome; it worked out of the box without any additional config steps, as described here, and gave me a nice line-by-line coverage report. Your install script seems very comprehensive, I'll definitely have a look into it. For my test environment, I so far just use the |
@drbarto I've been trying to make some API tests work using gulp-mocha and gulp-istanbul, but I can't seem to get any coverage. I've posted an issue here. Maybe you could take a look and let me know, if anything is obviously wrong with my code. I could post my code here as well, but I'm thinking this is more of an Istanbul, than a parse issue, so it shouldn't be here. Let me know, what you think. |
It looks like that you start your Parse server as a separate process and the unit test depends on having a running Parse server present. From my understanding, coverage checking can not work this way. Only when Cloud Code and the tests run in the same process, istanbul is able to observe which code paths are taken. Try to start your Parse server as part of the test setup:
Here is a simple test file which starts a server and initializes the Parse client in the
Finally, the gulp file for running the test with code coverage is very straightforward:
|
@drbarto Thanks for the comprehensive explanation. I followed these steps but I'm still getting 0% coverage from istanbul. Any ideas, what I might be missing? This is my test file: const chai = require('chai');
const chai_as_promised = require("chai-as-promised");
const expect = chai.expect;
const express = require('express');
const http = require('http');
const parse_server = require('parse-server').ParseServer;
const path = require('path');
const Parse = require('/usr/lib/node_modules/parse-server/node_modules/parse/node');
const app_id = "app_id";
const database_uri = "mongodb://";
const master_key = "masterkey";
const server_url = "http://localhost:1337/parse";
const headers = {"X-Parse-Application-Id": app_id, "Content-Type": "application/json"};
chai.use(chai_as_promised);
function startServer() {
return new Promise(function(resolve, reject) {
const api = new parse_server({
databaseURI: database_uri,
cloud: "/home/parse/cloud/main.js",
appId: app_id,
masterKey: master_key,
serverURL: server_url
});
const app = express();
// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));
// Serve the Parse API on the /parse URL prefix
app.use('/parse', api);
var httpServer = http.createServer(app);
httpServer.listen(1337, function(error) {
if (error) {
reject();
}
else {
resolve(httpServer);
}
});
});
}
function stopServer(httpServer) {
httpServer.close(function() {
console.log("Stopping server.");
});
}
// UNIT test begin
describe('Parse Test', function() {
var server;
beforeEach(function() {
return startServer().then(function(httpServer) {
Parse.initialize(app_id, "", master_key);
Parse.serverURL = 'http://localhost:1337/parse';
server = httpServer;
});
});
afterEach(function() {
return Promise.resolve().then(function() {
return stopServer(server)
});
});
it('should get constants', function(done) {
// bridging between JS promises required by mocha/chai
Promise.resolve()
.then(function() {
return Parse.Cloud.run('getConstants');
})
.then(function(result) {
expect(result["cover_height"]).to.equal(400);
done();
})
.catch(done); // will fail the test when CC returns an error
});
}); And the gulpfile: var gulp = require('gulp'),
mocha = require('gulp-mocha'),
istanbul = require('gulp-istanbul'),
JS_PATH_SERVER = "/home/parse/cloud_dir/cloud/",
TEST_PATH_SERVER = "./tests/";
function handleError(err) {
console.error(err);
}
gulp.task('pre-test', function () {
return gulp.src([JS_PATH_SERVER + '**/*.js'])
.pipe(istanbul({includeUntested: true}))
.pipe(istanbul.hookRequire());
});
gulp.task('run-test', ['pre-test'], function() {
return gulp.src(TEST_PATH_SERVER + '**/*.js', {read: false})
.pipe(mocha({reporter: 'spec'}))
.on("error", handleError)
.pipe(istanbul.writeReports());
}); |
Looks like you use different Cloud Code root directories in the test and in the gulpfile... you start your server with Btw -- I tried your code and after adding my server's config it worked without a problem. |
@drbarto They're both the same directory. Could you show me your server config if you're configuring it differently? Also, are you installing the packages locally or globally? I installed them using this command:
Really doubt there's a wrong way to install them, but just in case there's something more to it. FYI I'm using Ubuntu 14.04.5 LTS on a Droplet on DigitalOcean. |
As I said, your code worked without modifications (I just changed the hard-coded settings like paths to cloud code, mongo url etc.). I installed the node packages locally ( However, I run my test setup locally (that is, on my dev machine) and not on a remote host. Not sure if this could cause any problems... you can give it a try though, just install mongodb on your dev machine and adapt the test script accordingly. (Running the tests locally has the additional benefit of debugging, so IMO it's definitely worth the setup work.) |
@drbarto I'm not sure why it's still not running on my environment. I guess I'll have to test it locally, since this is currently the only difference. Although, it shouldn't differ whether it's tested locally or remotely, since when run remotely everything is run on the same environment. Just to eliminate any variables, I'm running the test using The cloud code is under |
Looks all good to me.. no idea what's blocking you, sorry. PS: Of course it should work on a remote server, I eventually plan to integrate the test code in a CI environment.. |
@drbarto Just for the record, it turned out it was an issue with one of the libraries I was using in my cloud code. |
@drbarto Martin, have you managed to find a way to drop the database in the teardown() of the unit test? I'm trying to achieve the same, and am stuck. I'm using swift on iOS. Worst case scenario I could get hold of my ids and class names and delete them after each test. But that's messy. Another problem is that deletion is async and could not finish in time before the next unit test has run, and hence the db won't be in a clean state. :( |
@houmie Nope, in my current setup I just leave the test data in the database. Occasionally I'll manually reset the test db (i.e. delete it and bootstrap a new db just with the required class schema). I plan to automate this process but currently can live with the manual step because I have to do it pretty rarely. In a way, not clearing the db after every test has a nice side effect: it ensures that my tests are independent of any pre-existing data and work stand-alone, no matter whether the db is fresh or crammed with thousands of test data sets. Concerning your issue with async deleting in |
@drbarto That was a great tip. So far it seems to be working nicely. Many Thanks. |
@drbarto Thanks for your tips, they are very helpful.
How do you pass a user to the function? Passing this dictionary |
@agordeev just make the request as the user, by passing the session token of that user into the
This way, checking for Check the docs of |
@natanrolnik Thanks Natan, that worked. Unfortunately, |
onebody please help me find the error on following code, my backend developer are quit, i can't test the cloud code Fri May 25 2018 10:18:07 GMT+0000 (UTC) code:
}); Parse.Cloud.define("notification", function(request, response) {
});` |
Is there a recommended way to unit-test Cloud Code functions? I couldn't find any libs or best practices that take into account the self-hosting capabilities of parse-server, and my related question on StackOverflow was ignored.
Any info on that topic from the Parse community would be appreciated!
The text was updated successfully, but these errors were encountered: