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

jankycheck

v0.0.1

Published

janky quickcheck for javascript

Downloads

3

Readme

jankycheck

Do not use this

At least don't use it for anything real. I make no promises about it working well. I mean, really, didn't you see the name?

If you must try it out

Install

$ npm install -g git://github.com/eventualbuddha/jankycheck.git

You don't have to install it globally, it's just easier that way.

Write some properties

Create a module with export whose names start with prop_. These should be functions that take however many random arguments you need and return a boolean to indicate success or failure. If the function takes arguments it should have a generators property containing an array of gentest generators. Here's an example:

// myprops.js
var gentest = require('gentest');

function add(x,y) {
  return x + y;
}

function prop_isCommutative(x, y) {
  return add(x, y) === add(y, x);
}

prop_isCommutative.generators = [gentest.types.int, gentest.types.int];
exports.prop_isCommutative = prop_isCommutative;
$ jankycheck myprops.js
✓ prop_isCommutative

1 total, 1 passed, 0 failed.

PDD (Property-Driven Development)

jankycheck uses gentest to handle the generation of random data to pass to properties under test. It also uses it to check the properties. jankytest intercepts failures and automatically shrinks the failing test case down to a minimal failing test using shrinker. Given this broken pow function and property:

var gentest = require('gentest');

function pow(base, exponent) {
  var result = 1;
  while (--exponent > 0) {
    result *= base;
  }
  return result;
}

function prop_powWorks(base, exponent) {
  return pow(base, exponent) === Math.pow(base, exponent);
}
prop_powWorks.generators = [gentest.types.int, gentest.types.int];
exports.prop_powWorks = prop_powWorks;

pow looks like it might work, but let's see what happens when we try it:

$ jankycheck myprops.js
  ✘ prop_powWorks

    Failed with test case: prop_powWorks(-1, -1)
    Shrunk after 2 iterations to prop_powWorks(0, 1)


  1 total, 0 passed, 1 failed.

We aren't handling a base of 0 correctly or negative exponents. Let's fix base of 0 first.

function pow(base, exponent) {
  if (base === 0) {
    return 0;
  }

  var result = 1;
  while (--exponent > 0) {
    result *= base;
  }
  return result;
}

Looks better! Let's try again.

$ jankycheck myprops.js
  ✘ prop_powWorks

    Failed with test case: prop_powWorks(0, -1)
    Shrunk after 1 iteration to prop_powWorks(0, 0)


  1 total, 0 passed, 1 failed.

Oh right, 00 is 1, not 0.

function pow(base, exponent) {
  if (exponent === 0) {
    return 1;
  }

  if (base === 0) {
    return 0;
  }

  var result = 1;
  while (--exponent > 0) {
    result *= base;
  }
  return result;
}

Okay, that should fix the zero exponent case.

$ jankycheck myprops.js
✘ prop_powWorks

  Failed with test case: prop_powWorks(-3, 3)
  Shrunk after 4 iterations to prop_powWorks(2, 1)


1 total, 0 passed, 1 failed.

Grr, something else is busted. Maybe --exponent should be exponent--?

function pow(base, exponent) {
  if (exponent === 0) {
    return 1;
  }

  if (base === 0) {
    return 0;
  }

  var result = 1;
  while (exponent-- > 0) {
    result *= base;
  }
  return result;
}

Let's see what that did…

$ jankycheck myprops.js
✘ prop_powWorks

  Failed with test case: prop_powWorks(4, -2)
  Shrunk after 2 iterations to prop_powWorks(0, -1)


1 total, 0 passed, 1 failed.

That's the problem we saw before with negative exponents! After fixing this we run into an issue where our results are not the same as Math.pow. This is probably attributable to floating point math weirdness. Also, what happens if base is 0 and exponent is less than 0? Division by zero! Turns out that's okay in JS as you just get Infinity. tl;dr, here's the final pow function and property:

var gentest = require('gentest');

function pow(base, exponent) {
  if (exponent < 0) {
    return pow(1/base, -exponent);
  }

  if (exponent === 0) {
    return 1;
  }

  if (base === 0) {
    return 0;
  }

  var result = 1;
  while (exponent-- > 0) {
    result *= base;
  }
  return result;
}

function prop_powWorks(base, exponent) {
  var result = pow(base, exponent);
  var reference = Math.pow(base, exponent);
  return result === reference || (Math.abs(result - reference) / reference) < 0.00000001;
}
prop_powWorks.generators = [gentest.types.int, gentest.types.int];
exports.prop_powWorks = prop_powWorks;

Let's run it just to make sure:

$ jankycheck myprops.js
✓ prop_powWorks

1 total, 1 passed, 0 failed.

The great thing about this was that we didn't have to keep thinking of test cases. Instead, they were just handed to us (in minimal form) and all we had to do was figure out why they failed. We did have to modify our property a little bit (yay floats!), but not much.

You're right that this is a contrived example since we had a reference implementation we could check our result against. But often you'll have a slow-but-working implementation and a fast-but-untested implementation that you can compare against each other. Also, you can do model-based property testing where you model the system under test using simple primitives you know work (such as using the built-in Array class to test your custom Set class).

Contributing

Well, probably don't. If you must, create a branch and submit a PR.

License

MIT, but you're not using this library for real, right?