Socket
Socket
Sign inDemoInstall

neo-forgery

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

neo-forgery

the easy way to mock a neo4j-driver session


Version published
Weekly downloads
2.7K
decreased by-12.79%
Maintainers
1
Weekly downloads
 
Created
Source

the easy way to mock a neo4j-driver session.

codecov Version Downloads/week License

Geenee Template

:clipboard: Why

I couldn't find any other straightforward way to mock the neo4j driver during unit tests. It needs to be super fast and simple to work with CI and TDD.

:white_check_mark: What

A mock session generator for neo4j. You set up a mock neo4j session by specifying an array of query spec objects. Each query spec object contains a query string, param set, and expected response.

You can then pass in your session as a parameter to a function to test instead of a real session. It now works for both running queries directly (session.run(...)) and transactions (session.readTransaction() and session.writeTransaction()).

And there's a function to test a query set against the live database, not intended for unit tests. That way, whenever you change your database you can confirm that the queries in your mock session are all still working!

:wrench: Usage

Include the package in dev:

npm i -D neo-forgery

Mocking a Query

To mock a query, simply:

  1. capture the result from the query. For instance, if you call the query in your code already with a console.log statement:
    (result: any) => {
       console.log(`result=${JSON.stringify(result)}`)
       ...
    }

Or you can run the query in the neo4j data browser, then on the left click the Code button and copy the Response:.

response

NOTE If you copy from the data browser, you'll only get the records portion of the output. You'll have to paste it in as a value for records in an object:

{
    records: <Response>
}
  1. copy and store the output as a const, e.g.:
const sampleOutput = {
  'records': [{ ... } ... ]
}
  1. create an array of QuerySpec and insert your query string, params, and output. Here's an example in TypeScript using the sample movies database.
import {QuerySpec, mockSessionFromQuerySet} from 'neo-forgery'

const querySet:QuerySpec[] = [
    {
        name: 'movies',
        query: 'MATCH (movie:Movie)' +
            ' WHERE toLower(movie.title) CONTAINS $query' +
            ' RETURN movie',
        output: expectedResultForMovieQuery,
        params: {query:"matrix"}
    },
    {
        name: 'title',
        query: 'MATCH (movie:Movie {title:$title}) ' +
            'OPTIONAL MATCH (movie)<-[rel]-(person:Person) ' +
            'RETURN movie.title as title, ' +
            'collect({name:person.name, role:rel.roles, job:head(split(toLower(type(rel)),\'_\'))}) as cast ' +
            'LIMIT 1',
        params: {title: 'Apollo 13'},
        output: expectedResultsForTitleQuery,
    }
]
  1. generate a mockSession that returns it using mockSession. You can then call mockResultsFromCapturedOutput to generate a true neo4j array of the Record type to compare the expected output to what your mock session returns.
    const session = mockSessionFromQuerySet(querySet)
    const output = await session.run(query, params)
    t.deepEqual(output,mockResultsFromCapturedOutput(expectedOutput))

You can pass your mock session into code that requires a session.

An alternative to mockResultsFromCapturedOutput is mockResultsFromData, which takes as input an array of objects containing record values. That can be useful if you know what data you want, and did not copy the Results from the data browser or from a console.log statement.

Checking the Validity of Your Mocked Queries

The neo-forgery package is build based on the premise that unit tests must be fast. By removing the need to query an actual database, you get instant results. But what if your database changes and the queries no longer work?

To solve that problem, neo-forgery exports a function:

async function testQuerySet(querySet: QuerySpec[], databaseInfo: DatabaseInfo)

You can pass in your set of queries along with the information needed for a database, and you can check whether the queries work for the given database. If any of them fail to return what you specify as output, and error is returned.

For example:

import { DatabaseInfo, testQuerySet } from 'neo-forgery'
const moviesDatabaseInfo: DatabaseInfo = {
  URI: 'neo4j+s://demo.neo4jlabs.com',
  USER: 'movies',
  PASSWORD: 'movies',
  DATABASE: 'movies',
};

const queriesWorking = await testQuerySet(querySet, moviesDatabaseInfo)
t.is(queriesWorking, "success")  

This function is not intended for unit tests! The whole point of neo-forgery is to remove the need to query an actual database when you run your unit tests. Rather, consider creating a separate test that you call regularly, perhaps as part of a regression test or after you change your database.

NOTE Currently, testQuerySet() currently checks only records in query results, and only makes an exact match. For instance, it will throw an error even the your output for a query is a subset of the data returned. That is a problem if you want to create small sample outputs for testing purposes. A future version of neo-forgery may remove that limitation by allowing you to specify a type of comparison for a query.

:paperclip: Data Types

There are some interfaces that you can import into your TypeScript project.

Database Specification

You must use a DatabaseInfo type for specifying the database that you want to use for running testQuerySet.

interface DatabaseInfo {
  URI: string;
  USER: string;
  PASSWORD: string;
  DATABASE?: string;
}

Query Response Specification

The output from a query is specified via a MockOutput instance:

interface MockOutput {
    records: SampleOutputRecord[];
    summary?: any;
}

Normally, you won't need to worry about the details for that. You can just capture the output in the ways specified above.

Query Specifications

A query set is an instance of QuerySpec[]:

export interface QuerySpec {
    name?: string;
    query: string;
    output: MockOutput;
    params?: ParamSet;
}

:key: Functions

Mock Results Generation

There are two functions for generating mock output. These can be used for confirming that output is what you expect it to be.

function mockResultsFromCapturedOutput(sampleOutput: MockOutput)

You can pass in results from a query as captured by a console.log statement, or based on results from the neo4j data browser.

mockResultsFromData(sampleResults: object[])

You can pass in an array of objects expected and it returns the mock results.

Mock Session Generation

There are two functions for mock session generations, analogs to the mock results generation functions above.

mockSessionFromQuerySet

You pass in a QuerySet and an instance of a neo4j Session is returned.

mockSessionFromFunction(sessionRunMock: Function)

Pass in a Function and a session is generated.

The sessionRunMock function shoud take in as parameters (query: string, params: any), and should normally return mock results. You can use the mock results generation functions above for the returned values. You can also vary the output from the function based upon the query and params received. In theory you could create a session which emulates your entire database for all of the queries in your tests, and simply reuse the mock session in all of your tests. Note that you can also throw errors if you are testing for them.

Verification of Your Mock Query Results

One last function is

testQuerySet(querySet: QuerySpec[], databaseInfo: DatabaseInfo)

See Checking the Validity of Your Mocked Queries above.

:heavy_exclamation_mark: Limits

Some limits to neo-forgery are intentional. There's no testing of data updates, because it should not be relevant to unit tests. You are not testing the database server, or even the queries themselves. A query is just a string from the standpoint of your unit, and your unit tests should assume that the response is predictable and unchanging. An end to end test can confirm that independently. The most that you might need from the standpoint of unit testing is to confirm that a write query was executed, which you can do with a sinon spy.

But neo-forgery is new, and there still are things that it should be able to do that have not yet been implemented.

  1. Currently, you can't have more than one result in a given mock session for a given query and parameter combination. That's limiting, because you might have a sequence that queries for something, changes it, and queries for the new value. For instance, your unit may check whether someone has a registered email, and if it's not there you may add it and then confirm that it is there and proceed with further steps based on that output. There is a planned implementation fixing that problem.

  2. The optional config parameter for a Session.run() is not supported currently. Much of the config may be irrelevant to unit testing, so that will probably be implemented as needed.

  3. The intended use case is when a session is passed in as a parameter. Arguably passing in a session is better style anyway in most cases. Doing so isolates entirely the session and database info from the queries being performed. But if your unit explicitly declares a session using neo4j-driver you will have to stub the driver. Here's an example of a stub:

    const neo4j = require('neo4j-driver');
    
    const mockDriver = () => {
      const driver = neo4j.driver(
        process.env.DB_URI,
        neo4j.auth.basic(
          process.env.DB_USER,
          process.env.DB_PASSWORD,
        ),
      );
    
      const session = mockSessionFromQuerySet(querySet);
        driver.session = () => session
        return driver;
    
      return driver;
    };
    
    

    You can then use proxyquire or whatever tool you'd like to stub out the definition of driver in your code and replace it with mockDriver.

    const { myUnit } = proxyquire('../src/myUnit', {
      'neo4j-driver': { 'driver': mockDriver },
    });
    

:blue_book:Tutorial

Check out this tutorial to create a project and test.

Keywords

FAQs

Package last updated on 06 Jul 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc