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

lawful-typeclasses

v0.5.1

Published

Property-based testing library. Inspired by principled type classes.

Downloads

12

Readme

Lawful Type Classes

lawful-typeclasses is a library designed to provide a way of asserting the behavior of your JavaScript classes.

"Lawful" here refers to a characteristic of principled type classes.

What it does

This library allows you to define two things: classes and instances. Perhaps a bit confusedly, classes are JavaScript objects and instances are JavaScript classes.

We'll be referring to the JavaScript classes that implement the behavior of a type class (and are thus instances of that class) as constructors and to the instances of those JavaScript classes as instance values.

What this library then allows you to do is to check if every constructor follows the rules defined in their classes, so that you are able to modularize your tests in a neat way.

Classes

A class is what defines the behavior that you want your instances to conform to.

For example, let's say that you want to define a class of things that can be added:

// our class is going to be an instance of Class
const addable = new Class({
  // this is what I've decided to name my class
  // this option is not necessary, but it helps to improve error messages
  name: 'Addable',
  // next, we define the properties we expect our instances to have.
  // we'll start out by using the `all` function to say that, in order to
  // be an Addable, the constructor must obey all of the following laws
  // (not just any)
  laws: all(
    // using named functions is not necessary, but it helps to improve error
    // messages as well
    // the first parameter is the instance constructor itself, the others are
    // random values of that constructor
    obey(function commutativity(Instance, x, y) {
      const a = x.add(y)
      const b = y.add(x)

      return a.equals(b)
    }),
    obey(function associativity(Instance, x, y, z) {
      const a = x.add(y.add(z))
      const b = x.add(y).add(z)

      return a.equals(b)
    }),
  ),
})

But, as you might have seen, we also expect our instances to implement an #equals method.

That could be another class:

const eq = new Class({
  name: 'Eq',
  laws: all(
    obey(function reflexivity(Instance, x) {
      return x.equals(x)
    }),
  ),
})

And then the Addable class may extend Eq, meaning that, in order to be an instance of Addable, the constructor must also be an instance of Eq:

const addable = new Class({
  name: 'Addable',
  extends: [eq],
  laws: // ...
})

Instances

Instances are JavaScript constructors that behave according to some (type) class.

Let's start with the following:

class Number {
  constructor(n) {
    this.n = n
  }

  equals(other) {
    return this.n === other.n
  }

  add(other) {
    return new Number(this.n + other.n)
  }
}

In order to declare it as an instance of something, you must provide a way of generating values from it. These are the values that will be used for testing. (See How it works)

There are two ways of doing that:

// you may ask for as many parameters as you want, and to each one will be
// assigned a random number between 0 and 1 (inclusive)
// from these numbers you may generate an instance of your constructor
const gen = continuous((n) => new Number(n))

// note that, to increase the likelihood of catching edge cases, sometimes the
// generated numbers will be all 0s or 1s
// testing values will be sampled from the given array
const gen = discrete([new Number(0), new Number(1), new Number(2)])

// this method would be more useful if we had a finite number of possible
// values, which is not the case

And then you only need to call instance with the correct parameters and the validators will run. You should call this at some point in your tests.

// will throw an Error if it fails
instance(Number, addable, gen)

Additionally, you may specify how many times each law will be tested (The default is 15 times):

instance(Number, addable, gen, { sampleSize: 10 })

When instance is called, a sample of random instance values will be created using your provided generator, and each class property will be tested using those. If any of the laws fails to be asserted, an error is thrown, and you may be sure that the constructor in question is not an instance of the class you declared.

In case it passes, you may have a high confidence that it is.