npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

test-logic

v1.1.1

Published

A collection of often-repeated test assertion patterns that I was tired of typing over and over again!

Downloads

14

Readme

test-logic

TestLogic is a collection of static helper classes for jest tests to help prevent code repetition and increase test writing speed.

Installation

Use npm to install the test-logic package. Include the --save-dev argument if this module will only be used in your tests.

npm install test-logic --save-dev

If using TypeScript, install type definitions.

npm install @types/test-logic --save-dev

Usage

Include options

Include the entire suite of test helper classes.

Example:

import TestLogic from 'test-logic';

Include specific test helper classes.

Example:

import { TestClassMethodResult } from 'test-logic/dist/testClassMethodResult';
import { TestClassMethodUsage } from 'test-logic/dist/testClassMethodUsage';
import { TestFunctionResult } from 'test-logic/dist/testFunctionResult';
import { TestFunctionUsage } from 'test-logic/dist/testFunctionUsage';
import { TestHelper } from 'test-logic/dist/TestHelper';

Using the TestHelper class

The TestHelper class provides convenient methods to format your existing fixtures for testing by one of the 'test usage' or 'test result' classes in TestLogic.

Using convertArgumentsToArray

TestLogic classes require you to provide function and method arguments in the form of an array where the array index corresponds to the argument's order in the function or method signature. This simple method adds readability to your tests by replacing an array declaration with a descriptive method indicating the original purpose of the source elements.

Example: Creating an argument array that is expressed in the same pattern as the signature of the function or method that will be tested.

import TestLogic from 'test-logic';

const firstArgument = 1;
const secondArgument = 'test';
const thirdArgument = false;

const argArray = TestLogic.TestHelper.convertArgumentsToArray(firstArgument, secondArgument, thirdArgument);

Instead of:

const argArray = [firstArgument, secondArgument, thirdArgument];

Using bundle methods for functions or class methods you wish to run during the test

Each bundle method helps you neatly package functions, classes and their arguments into objects needed by Testlogic classes.

Bundling the function 'myFunction' to run during the test with the arguments (123, 'test'):

import TestLogic from 'test-logic';
import { myFunction } from '.';

const argArray = TestLogic.TestHelper.convertArgumentsToArray(123, 'test');
const functionBundle = TestLogic.TestHelper.bundleTestFunction(myFunction, argArray);

Bundling the class method 'myClassInstance.myMethod' to run during the test with the arguments (123, 'test'):

import TestLogic from 'test-logic';
import { MyClass } from '.';

const myClassInstance = new MyClass();
const argArray = TestLogic.TestHelper.convertArgumentsToArray(123, 'test');
const classBundle = TestLogic.TestHelper.bundleTestClassMethod(myClassInstance, 'myMethod', argArray);

Using bundle methods to test for integration with functions or class methods you expect to be run by your code

When testing integration with other functions and class methods, bundle the expected interactions for use by TestLogic classes depending on the requirements of your test. The provided bundle methods of the TestHelper class each contain an optional 'timesOrArguments' as their final argument. The following uniform behavior applies to all bundle methods:

  • omitted or undefined: 0 calls will be expected to the function or class method being bundled.
  • number (num): The function or class method being bundled will be expected to have been called num times. Each call will be expected to have passed no arguments.
  • any[][] (argArray): The function or class method will be expected to have been called argArray.length times. A zero length array is permitted, but will be treated the same as being omitted or undefined. For non-zero length arrays, the first call will expect arguments of (...argArray[0]), the second (...argArray[1]), etc.
Called class method bundles

Classes used in these bundles should be imported, and if required instantiated, prior to bundling. When testing the use of a class instance by your method or function, the instance must be passed to the function, method (or in some cases the class constructor for non-static methods) in order for TestLogic to test integration.

Depending on your needs, the class can be mocked but it is not required. You do not need to provide jest spies.

Example: Bundling the expected use of the static method 'CalledClass.calledMethod' 2 times, first with the arguments (123, 'test') then with the arguments (432, 'complete'):

import TestLogic from 'test-logic';
import { CalledClass } from '.';

const expectedFirstArgArray = TestLogic.TestHelper.convertArgumentsToArray(123, 'test');
const expectedSecondArgArray = TestLogic.TestHelper.convertArgumentsToArray(432, 'complete');
const calledWithArray = TestLogic.TestHelper.convertArgumentsToArray(expectedFirstArgArray, expectedSecondArgArray);

const calledBundle = TestLogic.TestHelper.bundleCalledClassMethod(CalledClass, 'calledMethod', calledWithArray);

Alternatively, if you are not worried about line width:

const calledBundle = TestLogic.TestHelper.bundleCalledClassMethod(CalledClass, 'calledMethod', [expectedFirstArgArray, expectedSecondArgArray]);
Called function bundles

Functions used in these bundles should be imported and mocked, using jest.mock() to implement the behavior desired during the test.

Example: Bundling the expected use of the function 'myFunction' 1 time, with the arguments (123, 'test');

import TestLogic from 'test-logic';

const expectedArgArray = TestLogic.TestHelper.convertArgumentsToArray(123, 'test');

const calledBundle = TestLogic.TestHelper.bundleCalledFunction(myFunction, [calledWithArray]);

NOTE: The 2nd argument must still be an array of expected argument arrays. Take care when working with single calls, as array depth will be less obvious.

Using result test classes

The TestClassMethodResult and TestFunctionResult classes run a bundled class method or function and evaluate the results of the operation. Test classes include synchronous and asynchronous versions for each test type. The following methods are currently supported on each result test class:

  • runsWithoutError & runsWithoutErrorAsync: The bundled class method or function will execute with the given arguments and no error will be thrown.
  • runsWithError & runsWithErrorAsync: The bundled class method or function will execute with the given arguments and throw an error with a message equal to the expectedErrorMessage argument.
  • returnsExpected & returnsExpectedAsync: The bundled class method or function will execute with the given arguements returning a result equal to the expectedResult argument. Strict equality can be enabled with the useStrictEqual argument.

Using usage test classes

The TestClassMethodUsage and TestFunctionUsage classes run a bundled class method or function and evaluate its usage of a second bundled function or class. Usage test classes ignore any errors thrown by your tested class method or function, allowing you to test integration even in situations that are expected to generate an error. See important requirements on preparing the second bundle above, under "Using bundle methods to test for integration with functions or class methods you expect to be run by your code". Test classes include synchronous and asynchronous versions for each test type. The following methods are currently supported on each usage test class:

  • callsClassMethodTimes, callsClassMethodTimesAsync, callsFunctionTimes, callsFunctionTimesAsync: When executing the first bundle (class method or function with arguments) the function or class method within the second bundle will be called the specified number of times.
  • callsClassWithArguments, callsClassWithArgumentsAsync, callsFunctionWithArguments, callsFunctionWithArgumentsAsync: When executing the first bundle (class method or function with arguments) the function or class method within the second bundle will be called with each element of the specified arguments array.

Full Examples

Testing the result of your function

Your function, 'myFunction', should return a value of 'complete' when passed the arguments (1, true), 'idle' when passed (1, false), and 'processing' when passed anything else.

import TestLogic from 'test-logic';
import { myFunction } from '.';

const completeArgs = TestLogic.TestHelper.convertArgumentsToArray(1, true);
const idleArgs = TestLogic.TestHelper.convertArgumentsToArray(1, false);
const processingArgs = TestLogic.TestHelper.convertArgumentsToArray(20, true);
const completeResult = 'complete';
const idleResult = 'idle';
const processingResult = 'processing';
describe('When using myFunction', () => {
    test('and passing completeArgs, it returns complete', () => {
        const testBundle = TestLogic.TestHelper.bundleTestFunction(myFunction, completeArgs);
        TestLogic.TestFunctionResult.returnsExpected(testBundle, completeResult);
    });
    test('and passing idleArgs, it returns idle', () => {
        const testBundle = TestLogic.TestHelper.bundleTestFunction(myFunction, idleArgs);
        TestLogic.TestFunctionResult.returnsExpected(testBundle, idleResult);
    });
    test('and passing processingArgs, it returns processing', () => {
        const testBundle = TestLogic.TestHelper.bundleTestFunction(myFunction, processingArgs);
        TestLogic.TestFunctionResult.returnsExpected(testBundle, processingResult);
    });
});

Testing the usage of a function by your class method

Your class, 'MyClass' has a dependency of 'CalledClass'. 'MyClass.myMethod' has three arguments (count, minText, maxText) and should call 'CalledClass.calledMethod', a static method, as follows:

  • count = 0, calls 0 times
  • count greater than 0 but less than 10, calls 1 time with (1, minText) for arguments
  • count 10 or greater, calls 2 times with (1, minText) for the first argument set and (2, maxText) for the second argument set
import TestLogic from 'test-logic';
import { MyClass } from '.';
import { CalledClass } from 'called-class';

const myClass = new MyClass();
// used to call your class method in different ways
const zeroTimesArgs = TestLogic.TestHelper.convertArgumentsToArray(0, 'start', 'finish');
const oneTimeArgs = TestLogic.TestHelper.convertArgumentsToArray(4, 'start', 'finish');
const twoTimesArgs = TestLogic.TestHelper.convertArgumentsToArray(20, 'start', 'finish');
// variations in calls made to the dependency
const expectedFirstArg = TestLogic.TestHelper.convertArgumentsToArray(1, 'start');
const expectedSecondArg = TestLogic.TestHelper.convertArgumentsToArray(2, 'finish');
// expected calls to dependency in different tests
const expectedOneTimeArgs = TestLogic.TestHelper.convertArgumentsToArray(expectedFirstArg);
const expectedTwoTimesArgs = TestLogic.TestHelper.convertArgumentsToArray(expectedFirstArg, expectedSecondArg);

describe('When using an instance of MyClass', () => {
    describe('and passing arguments that should not result in a call to CalledClass.calledMethod', () => {
        const testBundle = TestLogic.TestHelper.bundleTestClassMethod(myClass, 'myMethod', zeroTimesArgs);
        const calledBundle = TestLogic.TestHelper.bundleCalledClassMethod(CalledClass, 'calledMethod');
        test('it is called 0 times', () => {
            TestLogic.TestClassMethodUsage.callsClassMethodTimes(testBundle, calledBundle);
        });
    });
    describe('and passing arguments that should result in a call to CalledClass.calledMethod one time', () => {
        // bundle the class with arguments that should result in a single call
        const testBundle = TestLogic.TestHelper.bundleTestClassMethod(myClass, 'myMethod', oneTimeArgs);
        // bundle the expected usage paramters for a single call
        const calledBundle = TestLogic.TestHelper.bundleCalledClassMethod(CalledClass, 'calledMethod', expectedOneTimeArgs);
        test('it is called 1 time', () => {
            TestLogic.TestClassMethodUsage.callsClassMethodTimes(testBundle, calledBundle);
        });
        test('with expected arguments', () => {
            TestLogic.TestClassMethodUsage.callsClassMethodWithArguments(testBundle, calledBundle);
        });
    });
    describe('and passing arguments that should result in a call to CalledClass.calledMethod two times', () => {
        // bundle the class with arguments that should result in two calls
        const testBundle = TestLogic.TestHelper.bundleTestClassMethod(myClass, 'myMethod', twoTimesArgs);
        // bundle the expected usage paramters for two calls
        const calledBundle = TestLogic.TestHelper.bundleCalledClassMethod(CalledClass, 'calledMethod', expectedTwoTimesArgs);
        test('it is called 2 times', () => {
            TestLogic.TestClassMethodUsage.callsClassMethodTimes(testBundle, calledBundle);
        });
        test('with expected arguments each time', () => {
            TestLogic.TestClassMethodUsage.callsClassMethodWithArguments(testBundle, calledBundle);
        });
    });
});

Contributing

Please suggest any changes via issue first to open a discussion. This project will be expanded, but no roadmap has been created at this time. Pull Requests are welcome if you find an bug and resolution, otherwise please also create issues for reporting bugs.

License

ISC