Files
elasticsearch-js/docs/testing.asciidoc
István Zoltán Szabó 9fe088589c [DOCS] Adds Integrations section to Node.JS docs (#1407) (#1419)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>

Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-03-11 07:51:15 +01:00

157 lines
4.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[[client-testing]]
=== Testing
Testing is one of the most important parts of developing an application.
The client is very flexible when it comes to testing and is compatible with
most testing frameworks (such as https://www.npmjs.com/package/ava[`ava`],
which is used in the examples below).
If you are using this client, you are most likely working with {es}, and one of
the first issues you face is how to test your application. A perfectly valid
solution is to use the real {es} instance for testing your application, but you
would be doing an integration test, while you want a unit test. There are many
ways to solve this problem, you could create the database with Docker, or use an
in-memory compatible one, but if you are writing unit tests that can be easily
parallelized this becomes quite uncomfortable. A different way of improving your
testing experience while doing unit tests is to use a mock.
The client is designed to be easy to extend and adapt to your needs. Thanks to
its internal architecture it allows you to change some specific components while
keeping the rest of it working as usual. Each {es} official client is composed
of the following components:
* `API layer`: every {es} API that you can call.
* `Transport`: a component that takes care of preparing a request before sending
it and handling all the retry and sniffing strategies.
* `ConnectionPool`: {es} is a cluster and might have multiple nodes, the
`ConnectionPool` takes care of them.
* `Serializer`: A class with all the serialization strategies, from the basic
JSON to the new line delimited JSON.
* `Connection`: The actual HTTP library.
The best way to mock {es} with the official clients is to replace the
`Connection` component since it has very few responsibilities and it does not
interact with other internal components other than getting requests and
returning responses.
[discrete]
==== `@elastic/elasticsearch-mock`
Writing each time a mock for your test can be annoying and error-prone, so we
have built a simple yet powerful mocking library specifically designed for this
client, and you can install it with the following command:
[source,sh]
----
npm install @elastic/elasticsearch-mock --save-dev
----
With this library you can create custom mocks for any request you can send to
{es}. It offers a simple and intuitive API and it mocks only the HTTP layer,
leaving the rest of the client working as usual.
Before showing all of its features, and what you can do with it, lets see an
example:
[source,js]
----
const { Client } = require('@elastic/elasticsearch')
const Mock = require('@elastic/elasticsearch-mock')
const mock = new Mock()
const client = new Client({
node: 'http://localhost:9200',
Connection: mock.getConnection()
})
mock.add({
method: 'GET',
path: '/'
}, () => {
return { status: 'ok' }
})
client.info(console.log)
----
As you can see it works closely with the client itself, once you have created a
new instance of the mock library you just need to call the mock.getConnection()
method and pass its result to the Connection option of the client. From now on,
every request is handled by the mock library, and the HTTP layer will never be
touched. As a result, your test is significantly faster and you are able to
easily parallelize them!
The library allows you to write both “strict” and “loose” mocks, which means
that you can write a mock that handles a very specific request or be looser and
handle a group of request, lets see this in action:
[source,js]
----
mock.add({
method: 'POST',
path: '/indexName/_search'
}, () => {
return {
hits: {
total: { value: 1, relation: 'eq' },
hits: [{ _source: { baz: 'faz' } }]
}
}
})
mock.add({
method: 'POST',
path: '/indexName/_search',
body: { query: { match: { foo: 'bar' } } }
}, () => {
return {
hits: {
total: { value: 0, relation: 'eq' },
hits: []
}
}
})
----
In the example above, every search request gets the first response, while every
search request that uses the query described in the second mock gets the second
response.
You can also specify dynamic paths:
[source,js]
----
mock.add({
method: 'GET',
path: '/:index/_count'
}, () => {
return { count: 42 }
})
client.count({ index: 'foo' }, console.log) // => { count: 42 }
client.count({ index: 'bar' }, console.log) // => { count: 42 }
----
And wildcards are supported as well.
Another very interesting use case is the ability to create a test that randomly
fails to see how your code reacts to failures:
[source,js]
----
mock.add({
method: 'GET',
path: '/:index/_count'
}, () => {
if (Math.random() > 0.8) {
return ResponseError({ body: {}, statusCode: 500 })
} else {
return { count: 42 }
}
})
----
We have seen how simple is mocking {es} and testing your application, you can
find many more features and examples in the
https://github.com/elastic/elasticsearch-js-mock[module documentation].