Welcome to Part 3 of this review of the Pluralsight course Node.js Testing Strategies by Rob Conery
Rob has been working in the technology field full time since 1998 as a DBA and then a web developer.
His original focus was the Microsoft ASP.NET stack, building tools like Subsonic and the first Micro-ORM: Massive. He co-founded the online training site Tekpub.com with James Avery and co-host This Developer’s Life with Scott Hanselman.
Tekpub was bought out by Pluralsight and he worked there for two years. He is currently working on a new book called The Imposters Handbook.
Also in this series:
Data Access Considerations
A Look as Node.js ORMs
We start this with Hogmanay, and why not? But back to business: we are looking at ORMs and data access with Node JS.
We start with Diogo Resende’s node-orm2 which supports MySQL, MariaDB, PostgreSQL, Amazon Redshift, SQLite and MongoDB.
Next up is Sequelize: a multi sql dialect ORM supporting MySQL, MariaDB, SQLite, PostgreSQL and MSSQL!
We also see Time Griesser’s Bookshelf JS which is used by the Ghost blogging platform.
Rob finds all of these are pretty much on a par with each other, working in much the same way.
However Rob is not generally a fan of ORMs. They can limit the full power of SQL by restricting you to only the subset of features that they support.
They are also another form of overhead that can get between you and the DBMS.
Stubbing with SinonJS
Database access is usually one of the slower operations in a program. This makes testing database access slower than we would like unit testing to be. Also data in a database tends to be more permanent than Random Access Memory, making reverting back to original state more difficult than in-memory operations.
These are a couple of reasons why we tend to write unit tests that don’t test the database. Instead, we use stubbed responses. SinonJS is one such tool that allows us to do that.
Rob discusses dependency injection. We pass in objects instead of creating them inside the function that we want to test. This allows us to stub or mock that object when writing our unit tests.
Rob explains sinon.stub and if you have done unit testing before this should be easy to understand. We can use it with the yields function to specify it’s behavior.
There are several other API functions that we might want to use, but yields is a good one to start with.
Setting up a Repository
Rob says a lot of .NET developers don’t like the repository pattern because it tends get out of control.
I have personally seen repository classes with well over 1,000 lines of code in them, amounting to a flagrant violation of the single responsibility principle. Developers could no longer tell whether the data access function they needed was already present in the repository, leading to further mistakes.
However, in the Node.JS environment, with modular code, it tends to work better.
Rob explains the design thinking that needs to go into designing our tests including how and where to set our stubs.
We can shoot ourselves in the foot by using stubs carelessly. Stubbing is a technique that should be used with caution.
Rob also warns against using the repository pattern over a large code base.
We see that Sinon’s restore function can help get us out of a sticky situation.
By the end of this lesson we have tests that work but are a bit smelly.
Making Things Easier with Helpers
This is a quick lesson in refactoring. We have repetition to eliminate.
We create helpers/index.js and put our validApplication object in it.
When Helpers Hurt
In this lesson we learn about to be wary of overuse of dependency injection. The goal should nearly always be to simplify our code.
This follows on to introducing inversion of control containers, and Rob introduces us to Nate Kohari’s Forge project.
Forge isn’t a very well known project, but Nate is also created Ninject which is one of the most famous IoC containers for .NET.
The main point that Rob makes though is what happens when you take the concept of helpers too far.
He moves the instantiation and stubbing of our database into a helper. Our repository is a singleton and we’re stubbing it.
He makes the point that it can be tempting to chase bad code with more bad code. When you’re in a hole, stop digging!
Listening to Your Tools
Halfway through this lesson we run our tests and see:
TypeError: Attempted to wrap getMissionByLaunchDate which is already wrapped
This is because the db repository is a global instance, i.e. a singleton. Singletons aren’t friendly to stubbing.
If we make DB a function and put our getMissionByLaunchDate function inside it, that works much better.
Work with repositories as instances, not as singletons.
Finishing up Review Process
We add another method in our repository called saveAssignment.
We stub this out for our tests, and our stub just says that it was saved.
Okay, our code is now well refactored and we can feel good about it. Bring out the fireworks – it’s time to celebrate!