While the Serverless Architecture introduces a lot of simplicity when it comes to serving business logic, some of its characteristics present challenges for testing. They are:
To get through these challenges, and to keep the test pyramid in mind, keep the following principles in mind:
Here is an example in Node.js of how to follow the practices above. The job this Function should perform is to save a user in a database and then send a welcome email:
const db = require('db').connect();
const mailer = require('mailer');
module.exports.saveUser = (event, context, callback) => {
const user = {
email: event.email,
created_at: Date.now(),
};
db.saveUser(user, function (err) {
if (err) {
callback(err);
} else {
mailer.sendWelcomeEmail(event.email);
callback();
}
});
};
There are two main problems with this function:
event
object).Let's refactor the above example to separate the business logic from the FaaS Provider.
class Users {
constructor(db, mailer) {
this.db = db;
this.mailer = mailer;
}
save(email, callback) {
const user = {
email: email,
created_at: Date.now(),
};
this.db.saveUser(user, function (err) {
if (err) {
callback(err);
} else {
this.mailer.sendWelcomeEmail(email);
callback();
}
});
}
}
module.exports = Users;
const db = require('db').connect();
const mailer = require('mailer');
const Users = require('users');
let users = new Users(db, mailer);
module.exports.saveUser = (event, context, callback) => {
users.save(event.email, callback);
};
Now, the above class keeps business logic separate. Further, the code responsible for setting up dependencies, injecting them, calling business logic functions and interacting with AWS Lambda is in its own file, which will be changed less often. This way, the business logic is not provider dependent, easier to re-use, and easier to test.
Further, this code doesn't require running any external services. Instead of a real db
and mailer
services, we can pass mocks and assert if saveUser
and sendWelcomeEmail
has been called with proper arguments.
Unit Tests can easily be written to cover the above class. An integration test can be added by invoking the function (serverless invoke
) with fixture email address, check if user is actually saved to DB and check if email was received to see if everything is working together.
Here are a few links to services which can help you test locally:
Product