JavaScript Testing Tool Showdown: Sinon.js vs testdouble.js
When unit testing real-world code, many situations make the test difficult to write. How to check if a function is called? How to test Ajax calls? Or use the code of setTimeout
? At this time, you need to use to test the stand-in - replace the code to make it easier to test within the hard-to-test.
For many years, Sinon.js has been the actual standard for creating test stand-ins in JavaScript testing. It is an essential tool for any JavaScript developer who writes tests, because without it, it is nearly impossible to write tests for real applications.
Recently, a new library called testdouble.js is emerging. It has a similar feature set to Sinon.js, except that there are a few differences here and there.
In this article, we will explore what Sinon.js and testdouble.js provide and compare their respective pros and cons. Is Sinon.js still a better choice, or can the challenger win?
Note: If you are not familiar with testing stand-ins, it is recommended that you read my Sinon.js tutorial first. This will help you better understand the concepts we will discuss here.
Key Points
- Feature Sets and Terminology: Sinon.js provides a traditional set of test stand-ins, including spies, stubs, and mocks, which are familiar to those who are proficient in other programming languages, while testdouble.js simplifies terminology and focuses on it In terms of JavaScript-centric vocabulary, traditional spies are eliminated.
- Easy to use: Testdouble.js has a simpler API that is easier for beginners to master, while Sinon.js provides a more detailed but more complex interface that users familiar with similar test tools in other languages may prefer it to others .
- Function of processing input: Sinon.js allows for more flexibility in stubs and assertions to use input parameters without necessarily requiring exact matches, which contrasts with the strict requirement of precise parameter matching by testdouble.js unless explicitly configured.
- Support for Promise and callbacks: Testdouble.js integrates built-in support for Promise and provides powerful callback handling, which may be more suitable for modern JavaScript development than Sinon.js, which requires additional information. Only by configuration can similar functions be achieved.
- Module Replacement Function: Testdouble.js supports replacing the entire module, which is conducive to isolating test module interactions; this function is not available in Sinon.js unless other tools such as proxyquire or rewire are used.
- Cleaning and Maintenance: Testdouble.js simplifies the cleaning process with a single reset function, reducing the risk of remaining test stand-ins that may affect other tests, while Sinon.js provides multiple cleaning methods that can be more troublesome to manage. .
The terms used in this article
To ensure easy understanding of what is being discussed, here is a quick overview of the terms used. These are the definitions of Sinon.js and may vary slightly elsewhere.
- Testing stand-in is a replacement for the functions used during testing. It can refer to any of the three types mentioned below.
- Spy is a test stand-in that allows the effect to be checked without affecting the behavior of the objective function.
- Stub is a test stand-in that replaces the behavior of the objective function with something else, such as the return value.
- Simulation is a different approach than stubs. The simulation contains built-in validation, which can be a substitute for separate assertions.
It should be noted that one of the goals of testdouble.js is to reduce confusion between this term.
Overview of Sinon.js and testdouble.js
Let's first look at the basic usage comparison between Sinon.js and testdouble.js.
Sinon has three different test stand-in concepts: spy, stubs, and mocks. The idea is that each represents a different usage scenario. This makes the library more familiar to people from other languages or who have read books using the same terminology, such as xUnit testing mode. But on the other hand, these three concepts may also make Sinon more difficult to understand when first used. This is a basic example of Sinon usage:
// 以下是查看函数调用的参数的方法: var spy = sinon.spy(Math, 'abs'); Math.abs(-10); console.log(spy.firstCall.args); // 输出:[ -10 ] spy.restore(); // 以下是控制函数执行方式的方法: var stub = sinon.stub(document, 'createElement'); stub.returns('not an html element'); var x = document.createElement('div'); console.log(x); // 输出:'not an html element' stub.restore();
In contrast, testdouble.js chooses a simpler API. Instead of using concepts like spies or stubs, it uses languages that JavaScript developers are more familiar with, such as td.function
, td.object
and td.replace
. This makes testdouble potentially easier to get started and more suitable for certain tasks. But on the other hand, some more advanced uses may simply not be possible (which is sometimes intentional).
The following is how to use testdouble.js:
// 以下是查看函数调用的参数的方法: var abs = td.replace(Math, 'abs'); Math.abs(-10); var explanation = td.explain(abs); console.log(explanation.calls[0].args); // 输出:[ -10 ] // 以下是控制函数执行方式的方法: var createElement = td.replace(document, 'createElement'); td.when(createElement(td.matchers.anything())).thenReturn('not an html element'); var x = document.createElement('div'); console.log(x); // 输出:'not an html element' // testdouble 使用一次调用重置所有测试替身,无需单独清理 td.reset();
testdouble uses a simpler language. We "replace" the function instead of "stub" it. We require the testdouble "interpretation" function to get information from it. Apart from that, it's quite similar to Sinon so far.
This also extends to creating "anonymous" test stand-in:
var x = sinon.stub();
and
var x = td.function();
Sinon's spies and stubs have attributes that provide more information about them. For example, Sinon provides attributes such as stub.callCount
and stub.args
. In the case of testdouble, we get this information from td.explain
:
// 我们也可以为测试替身命名 var x = td.function('hello'); x('foo', 'bar'); td.explain(x); console.log(x); /* 输出: { name: 'hello', callCount: 1, calls: [ { args: ['foo', 'bar'], context: undefined } ], description: 'This test double `hello` has 0 stubbings and 1 invocations.\n\nInvocations:\n - called with `("foo", "bar")`.', isTestDouble: true } */
One of the biggest differences is related to how stubs are set and verification is done. With Sinon, you can link commands after the stub and use assertions to verify the result. testdouble.js just show you how you want to call a function—or how to "rehearse" a function call.
var x = sinon.stub(); x.withArgs('hello', 'world').returns(true); var y = sinon.stub(); sinon.assert.calledWith(y, 'foo', 'bar');
and
var x = td.function(); td.when(x('hello', 'world')).thenReturn(true); var y = td.function(); td.verify(y('foo', 'bar'));
This makes the testdouble's API easier to understand, because you don't need to know what actions can be linked at when.
Compare common test tasks in more detail
At a high level, both libraries are quite similar. But what about common testing tasks you might need to perform in a real project? Let's see where some differences begin to appear.
testdouble.js No spy
The first thing to note is that testdouble.js does not have the concept of "spy". While Sinon.js allows us to replace function calls to get information from them while preserving the default behavior of the function, this is simply not possible in testdouble.js. When you replace the function with testdouble, it always loses its default behavior.
But this is not necessarily a problem. The most common usage of spies is to use them to verify that callbacks are called, which is easy to accomplish with td.function
:
var spy = sinon.spy(); myAsyncFunction(spy); sinon.assert.calledOnce(spy);
and
var spy = td.function(); myAsyncFunction(spy); td.verify(spy());
While it's not a big deal, it's still important to note this difference between the two libraries, otherwise you might be surprised if you expect to be able to use spies in testdouble.js in some more specific way.
testdouble.js requires more precise input
The second difference you will encounter is that testdouble is stricter on input.
Sinon's stubs and assertions allow you to be inaccurate with the provided parameters. This is easiest to illustrate by example:
var stub = sinon.stub(); stub.withArgs('hello').returns('foo'); console.log(stub('hello', 'world')); // 输出:'foo' sinon.assert.calledWith(stub, 'hello'); // 没有错误
and
// 以下是查看函数调用的参数的方法: var spy = sinon.spy(Math, 'abs'); Math.abs(-10); console.log(spy.firstCall.args); // 输出:[ -10 ] spy.restore(); // 以下是控制函数执行方式的方法: var stub = sinon.stub(document, 'createElement'); stub.returns('not an html element'); var x = document.createElement('div'); console.log(x); // 输出:'not an html element' stub.restore();
By default, Sinon does not care how many extra parameters are provided to the function. Although it provides functions such as sinon.assert.calledWithExactly
, it is not recommended as the default value in the documentation. Functions like stub.withArgs
also do not have "exactly" variants.
Testdouble.js, on the other hand, requires the specified exact parameters by default. This is by design. The idea is that if the function is provided with some other parameters that are not specified in the test, this may be an error and should fail the test.
Arbitrary parameters can be specified in testdouble.js, but this is not the default value:
// 以下是查看函数调用的参数的方法: var abs = td.replace(Math, 'abs'); Math.abs(-10); var explanation = td.explain(abs); console.log(explanation.calls[0].args); // 输出:[ -10 ] // 以下是控制函数执行方式的方法: var createElement = td.replace(document, 'createElement'); td.when(createElement(td.matchers.anything())).thenReturn('not an html element'); var x = document.createElement('div'); console.log(x); // 输出:'not an html element' // testdouble 使用一次调用重置所有测试替身,无需单独清理 td.reset();
Use ignoreExtraArgs: true
, the behavior is similar to Sinon.js.
testdouble.js has built-in Promise support
While using Sinon.js's Promise is not complicated, testdouble.js has built-in methods to return and reject Promise.
var x = sinon.stub();
and
var x = td.function();
Note: You can use sinon-as-promised to include similar convenient functions in Sinon 1.x. Sinon 2.0 and newer versions include Promise support in the form of stub.resolves
and stub.rejects
.
The callback support for testdouble.js is more powerful
Sinon and testdouble both provide an easy way to make the stub function callbacks. However, there are some differences in how they work.
Sinon uses stub.yields
to make the stub call the first function received as a parameter.
// 我们也可以为测试替身命名 var x = td.function('hello'); x('foo', 'bar'); td.explain(x); console.log(x); /* 输出: { name: 'hello', callCount: 1, calls: [ { args: ['foo', 'bar'], context: undefined } ], description: 'This test double `hello` has 0 stubbings and 1 invocations.\n\nInvocations:\n - called with `("foo", "bar")`.', isTestDouble: true } */
last parameter. You don't have to specify it when rehearsing the call:
var x = sinon.stub(); x.withArgs('hello', 'world').returns(true); var y = sinon.stub(); sinon.assert.calledWith(y, 'foo', 'bar');
Suppose we want to call
…callback1
var x = td.function(); td.when(x('hello', 'world')).thenReturn(true); var y = td.function(); td.verify(y('foo', 'bar'));
as a parameter to the function in td.callback
. This tells testdouble which parameter we want to use as the callback. td.when
var spy = sinon.spy(); myAsyncFunction(spy); sinon.assert.calledOnce(spy);
instead of callsArgWith
. We have to provide a specific index of the call to make it work, which can be a bit cumbersome, especially on functions with many parameters. yields
two callbacks with certain values?
var spy = td.function(); myAsyncFunction(spy); td.verify(spy());
, but it will only call one of them. callsArgWith
In addition to being able to replace functions with
, testdouble also allows you to replace the entire module. td.replace
var stub = sinon.stub(); stub.withArgs('hello').returns('foo'); console.log(stub('hello', 'world')); // 输出:'foo' sinon.assert.calledWith(stub, 'hello'); // 没有错误
, for example...td.replace('path/to/file')
// 以下是查看函数调用的参数的方法: var spy = sinon.spy(Math, 'abs'); Math.abs(-10); console.log(spy.firstCall.args); // 输出:[ -10 ] spy.restore(); // 以下是控制函数执行方式的方法: var stub = sinon.stub(document, 'createElement'); stub.returns('not an html element'); var x = document.createElement('div'); console.log(x); // 输出:'not an html element' stub.restore();
Although Sinon.js can replace member functions of an object, it cannot replace modules like this. To do this when using Sinon, you need to use another module, such as proxyquire or rewire.
// 以下是查看函数调用的参数的方法: var abs = td.replace(Math, 'abs'); Math.abs(-10); var explanation = td.explain(abs); console.log(explanation.calls[0].args); // 输出:[ -10 ] // 以下是控制函数执行方式的方法: var createElement = td.replace(document, 'createElement'); td.when(createElement(td.matchers.anything())).thenReturn('not an html element'); var x = document.createElement('div'); console.log(x); // 输出:'not an html element' // testdouble 使用一次调用重置所有测试替身,无需单独清理 td.reset();
Another thing you need to note about module replacement is that testdouble.js will automatically replace the entire module. If it is a function export like the example here, it replaces the function. If it is an object containing multiple functions, it replaces all of them. Constructors and ES6 classes are also supported. Both proxyquire and rewire require that you specify individually what and how to replace.
testdouble.js missing some helper functions for Sinon
If you use Sinon's emulation timer, emulation XMLHttpRequest, or emulation server, you will notice that they do not exist in the testdouble.
The emulation timer can be used as a plugin, but the XMLHttpRequests and Ajax functions need to be handled in different ways.
A simple solution is to replace the Ajax function you are using, e.g. $.post
:
var x = sinon.stub();
It is easier to clean up test content using testdouble.js
A common stumbling block for beginners in Sinon.js is often clean up spies and stubs. Sinon offers three different ways to do this, which doesn't help much.
var x = td.function();
// 我们也可以为测试替身命名 var x = td.function('hello'); x('foo', 'bar'); td.explain(x); console.log(x); /* 输出: { name: 'hello', callCount: 1, calls: [ { args: ['foo', 'bar'], context: undefined } ], description: 'This test double `hello` has 0 stubbings and 1 invocations.\n\nInvocations:\n - called with `("foo", "bar")`.', isTestDouble: true } */
var x = sinon.stub(); x.withArgs('hello', 'world').returns(true); var y = sinon.stub(); sinon.assert.calledWith(y, 'foo', 'bar');
methods, otherwise it is easy to accidentally leave stubs or spies, which may cause problems with other tests. This can lead to hard-to-trace cascade failures. sinon.test
. The recommended method is to call it in the td.reset()
hook: afterEach
var x = td.function(); td.when(x('hello', 'world')).thenReturn(true); var y = td.function(); td.verify(y('foo', 'bar'));
Pros and cons
We have now understood the functionality of these two libraries. They both offer a fairly similar set of features, but they have different design ideas from each other. Can we break it down into advantages and disadvantages?
Let's talk about Sinon.js first. It provides some more additional features than testdouble.js and some aspects of it are easier to configure. This provides some higher flexibility for it in more special testing scenarios. Sinon.js also uses the language of people who are more familiar with other languages—concepts such as spies, stubs and mocks exist in different libraries and has also been discussed in test-related books.
The disadvantage is that it increases complexity. While its flexibility allows experts to do more things, it means that certain tasks are more complex than in testdouble.js. It may also have a steeper learning curve for those new to test the concept of a stand-in. In fact, even someone as familiar with it as me may have a hard time explaining in detail some of the differences between
and sinon.stub
! sinon.mock
testdouble.js selected a simpler interface. Most of its content is fairly simple and easy to use and feels better suited to JavaScript, while Sinon.js sometimes feels like it's designed for other languages. Thanks to this and some of the design principles, it is easier for beginners to get started, and even experienced testers will find many tasks easier to complete. For example, testdouble uses the same API to set up test stand-ins and verify results. It may also be less prone to errors due to its simpler cleaning mechanism.
testdouble The biggest problem is caused by some of its design principles. For example, a complete lack of spies may make it impossible for some people who prefer to use spies over stubs. This is largely a question of opinion and you may not find the problem at all. Besides that, although testdouble.js is an updated library, it is providing some serious competition for Sinon.js.
Compare by function
The following is a comparison by function:
功能 | Sinon.js | testdouble.js |
---|---|---|
间谍 | 是 | 否 |
存根 | 是 | 是 |
延迟存根结果 | 否 | 是 |
模拟 | 是 | 是1 |
Promise 支持 | 是(在 2.0 中) | 是 |
时间辅助函数 | 是 | 是(通过插件) |
Ajax 辅助函数 | 是 | 否(改为替换函数) |
模块替换 | 否 | 是 |
内置断言 | 是 | 是 |
匹配器 | 是 | 是 |
自定义匹配器 | 是 | 是 |
参数捕获器 | 否2 | 是 |
代理测试替身 | 否 | 是 |
- testdouble.js Technically speaking There is no simulation like that in Sinon.js. However, since the simulation in Sinon is essentially an object that contains stubs and validation, similar effects can be achieved by using
td.replace(someObject)
. - can be achieved similar effects as parameter capturers by using
stub.yield
(not to be confused withstub.yields
).
Summary and Conclusion
Sinon.js and testdouble.js both provide a fairly similar set of features. In this regard, neither is obviously superior.
The biggest difference between the two is their API. Sinon.js is probably a little bit longer and offers many options on how to do things. This may be its pros and cons. testdouble.js has a leaner API, which makes it easier to learn and use, but due to its more arbitrary design, some may find it problematic.
So which one is suitable for me?
Do you agree with the design principles of testdouble? If so, then there is no reason not to use it. I've used Sinon.js in many projects and I can safely say that testdouble.js does at least 95% of the work I've done in Sinon.js and the remaining 5% may be done with some simple workaround.
If you find Sinon.js difficult to use, or are looking for a more "JavaScript-style" testdouble.js might be for you as well. Even people like me who spend a lot of time learning to use Sinon, I tend to suggest trying testdouble.js and see if you like it.
However, some aspects of testdouble.js may cause headaches for those with knowledge of Sinon.js or other experienced testers. For example, the complete lack of spies may be the decisive factor. Sinon.js is still a good choice for experts and those who want the most flexibility.
If you want to learn more about how to use test stand-ins in practice, check out my free Sinon.js guide. While it uses Sinon.js, you can also apply the same techniques and best practices to testdouble.js.
Are there any problems? Comment? Are you already using testdouble.js? After reading this article, would you consider giving it a try? Please let me know in the comments below.
This article was reviewed by James Wright, Joan Yin, Christian Johansen and Justin Searls. Thanks to all SitePoint peer reviewers for getting SitePoint content to its best!
Frequently Asked Questions about JavaScript Test Tools: Sinon.js vs. Testdouble.js
What is the main difference between Sinon.js and Testdouble.js?
Sinon.js and Testdouble.js are both popular JavaScript test libraries, but they have some key differences. Sinon.js is known for its rich feature set, including spies, stubs, and simulations, as well as utilities for emulating timers and XHR. It is a versatile tool that can be used in conjunction with any testing framework. On the other hand, Testdouble.js is a minimalist library that focuses on providing a simple and intuitive API for testing stand-ins, which are alternatives to the parts of the system to be tested. It does not include utilities for emulating timers or XHRs, but it is designed to be easy to use and understand, so it is a great option for those who prefer a leaner method of testing.
How to install Sinon.js and Testdouble.js?
Sinon.js and Testdouble.js can be installed through npm (Node.js package manager). For Sinon.js, you can use the command npm install sinon
. For Testdouble.js, the command is npm install testdouble
. After installation, you can use const sinon = require('sinon')
and const td = require('testdouble')
to introduce them in your test files, respectively.
Can I use Sinon.js and Testdouble.js at the same time?
Yes, Sinon.js and Testdouble.js can be used simultaneously in the same project. They are all designed to be very neatly simple and work well with other libraries. However, remember that they have overlapping features, so using them at the same time can lead to confusion. It is usually recommended to choose one of them based on your specific needs and preferences.
How to create a spy using Sinon.js and Testdouble.js?
In Sinon.js, you can use sinon.spy()
to create spies. This function returns a spy object that records all calls made to it, including parameters, return values, and exceptions. In Testdouble.js, you can create spies using td.function()
. This function returns a test stand-alone function that records all calls, including parameters.
How to create stubs using Sinon.js and Testdouble.js?
In Sinon.js, you can create stubs using sinon.stub()
. This function returns a stub object that behaves like a spy, but it also allows you to define its behavior, such as specifying a return value or throwing an exception. In Testdouble.js, you can create stubs using td.when()
. This function allows you to define your behavior when calling a test standby with a specific parameter.
How to use Sinon.js and Testdouble.js to verify spies or stubs?
In Sinon.js, you can use methods such as spy.called
, spy.calledWith()
, and spy.returned()
to verify spies or stubs. In Testdouble.js, you can use td.verify()
to assert whether the test standby is called in some way.
What are the advantages of using Sinon.js instead of Testdouble.js?
Sinon.js has a more comprehensive feature set compared to Testdouble.js. It includes utilities for emulating timers and XHR, which is very useful for testing certain types of code. It is also used more widely and has a larger community, which means more resources and support can be obtained.
What are the advantages of using Testdouble.js instead of Sinon.js?
Testdouble.js has a simpler and more intuitive API than Sinon.js. It is designed to be easy to use and understand, so it is a great choice for those who prefer a more streamlined test method. It also encourages good testing practices by making it difficult to abuse test stand-ins.
Can I use Sinon.js and Testdouble.js with other test frameworks?
Yes, Sinon.js and Testdouble.js are both designed very neatly and work well with other test frameworks. They can be used with any JavaScript-enabled test framework.
What resources are there to learn more about Sinon.js and Testdouble.js?
Yes, Sinon.js and Testdouble.js have a lot of documentation on their official websites. There are also many tutorials, blog posts, and online courses covering in-depth content from these libraries.
The above is the detailed content of JavaScript Testing Tool Showdown: Sinon.js vs testdouble.js. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











Frequently Asked Questions and Solutions for Front-end Thermal Paper Ticket Printing In Front-end Development, Ticket Printing is a common requirement. However, many developers are implementing...

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

There is no absolute salary for Python and JavaScript developers, depending on skills and industry needs. 1. Python may be paid more in data science and machine learning. 2. JavaScript has great demand in front-end and full-stack development, and its salary is also considerable. 3. Influencing factors include experience, geographical location, company size and specific skills.

Discussion on the realization of parallax scrolling and element animation effects in this article will explore how to achieve similar to Shiseido official website (https://www.shiseido.co.jp/sb/wonderland/)...

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

How to merge array elements with the same ID into one object in JavaScript? When processing data, we often encounter the need to have the same ID...

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

Explore the implementation of panel drag and drop adjustment function similar to VSCode in the front-end. In front-end development, how to implement VSCode similar to VSCode...
