Js Unit Testing Patterns
Js Unit Testing Patterns
Not long ago I wrote about the anatomy of a unit test along with helpful guidelines and
test-double concepts. Today Iʼm going to walk you through the common unit testing
patterns that I follow.
https://raygun.com/blog/unit-testing-patterns/ 1/11
Result test pattern
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
Whether or not youʼve ever written a unit test before, you can probably get whatʼs going
on here. I take the value 0, pass it into the Increment method, and then assert that the
result is incremented to 1.
vector.Normalize(); // Act
Although Iʼve listed these first two unit testing patterns under different headings, they
are both very similar and equally simple. You set up some kind of input, call a method,
and assert the expected result.
The input may be a primitive value, an object with a specific state, or sometimes nothing
at all.
The value you assert could be the return value of the method, an out parameter, or the
state of the original object acquired through calling a property or method. All of these
scenarios fall into the result/state test patterns. In cleanly structured code, all if not most
unit tests will be this simple to write.
https://raygun.com/blog/unit-testing-patterns/ 2/11
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
In general, you write many unit tests for each method you are testing in order to cover all
code paths, and to assert that various inputs produce the expected result. You donʼt,
and usually canʼt, test all possible inputs, but instead will want to test a variety of types
of inputs such as negative, positive and zero numbers, large and small values and so on.
Unit testing frameworks can also provide a way for you to test multiple inputs through a
single unit test by providing the inputs in a test method attribute.
Hereʼs an example of using the above builder by chaining the methods to build a Person
object:
Person testPerson = new PersonBuilder()
.SetFirstName("Jason")
https://raygun.com/blog/unit-testing-patterns/ 3/11
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
.SetLastName("Fauchelle")
.Build();
Outside of unit testing, the most common reason Iʼve seen for using the builder pattern
is to replace constructors with long parameter lists, especially when a lot of the
parameters have the same type. By looking at the invocation of such constructors, itʼs
not always clear what each parameter is for.
In C# at least, you donʼt need to specify the name of each parameter, and so you may
need to look into the constructor definition to find out what each parameter really is. In
the simplified example above though, using a builder is very clear to read as setting each
piece of the object state is spelled out for you. So this is great for unit tests, because we
always want them to be clear to read.
If arranging the state for several unit tests is a bit messy or complicated, then have a go
at using the fluent builder pattern. The state under test becomes very clear to read. In
some cases, using the fluent builder pattern in unit tests can help to prevent code
duplication, because a single method on the builder could encapsulate a lot of state-
setup code that can be written once and used by many tests.
This state-setup code is hidden away behind methods with clear names, which is all you
need to know when reading a unit test to understand what is being tested.
You are not limited to using Set methods on the builder, and the Build method is not
limited to only calling a constructor. Arranging the state of the object may also involve
calling methods that add objects to child collections, or void-parameterless methods
that update the objects state in some way. If the project you are testing already provides
a fluent builder, you might be able to use that, otherwise add one to the unit test project.
database. Like any unit test, you can assert that results returned from the function
youʼre testing is expected, or verify that certain methods are called on the unit of work –
depending on if you prefer state vs implementation testing.
Find a database mocking framework
If youʼre project doesnʼt use an ORM – or at least nothing that provides an interface to
fake/mock, then another option could be to use a database mocking framework. These
are frameworks that aid in mocking databases, so that again you can test queries
without actually connecting to a database. I havenʼt used any myself, nor looked too
much into how well this approach works so I canʼt comment further, but wanted to point
it out if youʼre interested in exploring this path.
Create a testing database
The main remaining option is to resort to setting up and connecting to a test database.
Please note that despite the name of this blog post, we are now delving into ‘integration
testingʼ territory – because instead of testing a single unit by itself, we are testing the
function of a unit while it is connected to an external technology. I personally find that
this is a good path to take though, especially when raw database queries are involved.
To test the function of a unit, setup a database full of all the test data youʼll need before
running any tests. Once this is done, the tests will:
Create the unit under test
Tell it to use the test database
Call methods on the units
Only add data to the database when the tests need it
Keeping all the possible testing data in a single test database is going to stack up quite a
bit over time. If you need to debug why a test has started to fail because of a recent
change youʼd need to sift through all the data and follow the indices to understand the
state under test.
When writing new tests, youʼd have the same problem sifting through the data to see if
the state that you want to test exists, or otherwise you need to add more data. An
alternative then is to set up just the data you need on a per-test basis. Or better yet, set
up data that you need to support several tests within the test fixture setup method. The
database itself can still be set up once before all tests, and could contain common data.
By wrapping up the per-test or per-fixture data in a transaction, you can then roll it back
when no longer needed. Overall this means the state under test is more clearly outlined
within the appropriate context, but the downside of course is that with more database
interaction, the testing process will be slower compared to setting up the database once
full of all testing data.
[TestFixture]
public class Execute_SingleSession
https://raygun.com/blog/unit-testing-patterns/ 5/11
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
{
private LightSpeedContext _context;
private IDataNodeModelUnitOfWork _unitOfWork;
private IDbTransaction _dbTransation;
[TestFixtureSetUp]
public override void TestFixtureSetUp()
{
_context = new LightSpeedContext("LightSpeedContext");
_unitOfWork = _context.CreateUnitOfWork();
_dbTransation = _unitOfWork.BeginTransaction();
[Test]
public void SessionIdIsCorrect()
{
var sessionForCrash = _query.Execute(_unitOfWork, 1, "jason@r
Assert.AreEqual(1, sessionForCrash.SessionId);
}
[TestFixtureTearDown]
public void FixtureTearDown()
{
_dbTransation.Rollback();
_dbTransation.Dispose();
_unitOfWork.Dispose();
}
}
To improve this, we can create a fluent builder for each type of record weʼll add to the
database to encapsulate the insert query:
public class DatabaseSessionBuilder
{
private int _id;
private string _country;
private DateTime _start;
After preparing all the individual strings from the various builders, append them together
and run as a single query. The result of this implementation is that the query for each
type of record now only exists in one place. If a database change occurs, fixing
thousands of unit tests should only require changing a few lines.
Another perk we get out of this implementation is clear state setup code. We can now
clearly see what each value represents because itʼs written out in the builder methods.
https://raygun.com/blog/unit-testing-patterns/ 7/11
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
The query string has a list of various numeric, string and date time values whose
purpose and ordering previously needed to be looked up in the database schema.
https://raygun.com/blog/unit-testing-patterns/ 8/11
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
Using this class in a unit test is going to cause it to try and connect to a real RabbitMQ
host, which as mentioned above is what weʼre wanting to avoid when writing unit tests.
We need to remove the need for running RabbitMQ from the equation, and making this
possible generally comes down to the use of interfaces in the software you are testing.
Use interfaces to separate external system implementations from your business
logic
Using the same example above, letʼs say the class we are testing instead takes an
IConnectionFactory into the constructor. The class then uses this to get an IChannel and
publish messages into it.
https://raygun.com/blog/unit-testing-patterns/ 9/11
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
In the unit tests, rather than using a ConnectionFactory provided by the RabbitMQ
library, weʼll pass in either a fake or a mock of IConnectionFactory and any surrounding
classes we need. I covered fakes and mocks in the previous post, but here is a quick
recap:
A fake is our own implementation that functions just enough for the unit tests to work.
Rather than connecting to a RabbitMQ host though, we can have an in-memory queue to
store published messages, and then assert that the message exists.
A mock is an object that we can setup with hardcoded responses to various method
calls. We can use this to verify that the BasicPublish method on an IChannel was called
appropriately.
By using an interface in the constructor, weʼre able to separate the specifics of of using
RabbitMQ from the surrounding logic within the unit. Now we can focus on testing just
that logic such as processing the inputs to result in correct messages being posted to
the appropriate queues – all without RabbitMQ actually being involved.
Create an interface if one doesnʼt exist
Sometimes, you may come across third party tech/service integration frameworks that
donʼt provide interfaces for the classes that communicate with the external software. To
help with unit testing your classes that use such integrations, you can create your own
interface so that you can follow the pattern outlined above. You can add whatever
methods you want to this interface based on the ways that you will be communicating
with the external software. Then, implement this interface, using whatever the
integration framework provides.
This implementation should be as minimal as possible. It will only be used as a means to
communicate with the external software, and will not contain any of your business logic
such as whether or not messages should be sent, or acting upon messages received.
We will not be writing unit tests for this class because it will require a connection to the
real external software, so integration tests would be more suitable. We created this class
https://raygun.com/blog/unit-testing-patterns/ 10/11
24/08/2019 Unit testing patterns: common patterns to follow for error free applications · Raygun Blog
due to the need for an interface to fill in what the framework did not provide for us. With
this interface, we can now use it in the constructor of the class that will contain the
business logic around the integration. Use the unit testing pattern above to test this
business logic.
Refactor code to make this unit testing pattern possible
If you come across a class in your codebase that you need to test which is somewhat
hard coded to interact directly with external software, have a go at refactoring it to pass
an interface into the constructor. While describing this pattern, Iʼve been talking about
integrations with external software, but this pattern can be applied to classes within the
same system too. Any interfaces passed into a unit youʼre testing is best to fake or
mock. If a class is creating instances of other classes internally that have various
responsibilities such as memory management or processing, you could refactor those
out to pass interfaces of those responsibilities into the constructor. This allows you to
test the logic of just this one unit, and also means the class isnʼt coupled to a single
implementation of those responsibilities.
Just because weʼve been doing this work to make unit testing these classes easier,
doesnʼt mean integration tests are out the door. The unit tests are great for testing that
our business logic interacts correctly with an external integration, as long as itʼs
functioning in an expected way from the perspective of our business logic class. Our
fakes or mocks are setup to respond to and return results that we know it should be
doing. Integration tests are still valuable to test that, after updating them, they still
accept and return messages that our software expects.
https://raygun.com/blog/unit-testing-patterns/ 11/11