如何用玩笑测试随机颜色生成器

How to test a random color generator with jest

我这里有一个函数returns一个随机十六进制颜色

function randomHex() {
  return `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")}`;
}

我怎样才能开玩笑地测试它? 我想知道它是否真的 returns 随机十六进制

也许是这样的

const { randomHex } = require('../../../');

describe('', () => {
  it('', () => {
    const firstRandomColor = randomHex();
    const secondRandomColor = randomHex();
    const hexRegex = /^#(?:[0-9a-fA-F]{3}){1,2}$/;

    expect(firstRandomColor).toMatch(hexRegex);
    expect(secondRandomColor).toMatch(hexRegex);
    expect(firstRandomColor).not.toBe(secondRandomColor);
  })
})

引用我自己,来自this post

So we can think about how to test it, here's a basic implementation of a coin flip:

const flipCoin = () => Math.random() < 0.5 ? "heads" : "tails";

We can be pretty confident that if we call this function a large number of times we'll get roughly half of each outcome:

> Array(10).fill(null).map(() => flipCoin());
[ "tails", "heads", "heads", "tails", "tails", "tails", "heads", "heads", "tails", "tails" ]

but for a given call we can't be sure which it will be. So how can we write a test for that? We could reach for the facade pattern again, extract const random = () => Math.random() and replace that with a test double. This would work fine, but be very tightly coupled to the implementation:

describe("flipCoin", () => {
  it("returns 'heads' when the random number is less than 0.5", () => {
    random.mockReturnValue = 0.3;

    expect(flipCoin()).toEqual("heads");
  });
});

One alternative is to write tests based on the properties of the implementation we want. For example, although we don't know the specific values, we do know:

  • It should always give one of the expected outcomes; and
  • It shouldn't always give the same outcome (otherwise () => "heads" would be a valid implementation).

在这种情况下,property-based 测试可能如下所示:

describe("randomHex", () => {
  it("always returns a colour", () => {
    const colours = Array(100).fill(null).map(() => randomHex());

    colours.every((colour) => expect(colour).toMatch(/^#[\da-f]{6}$/));
  });

  it("doesn't always return the same colour", () => {
    const colours = Array(100).fill(null).map(() => randomHex());
   
    expect(new Set(colours).size).toBeGreaterThan(1);
    // or a higher number, but e.g. `.toEqual(colours.length)` can fail due to collisions
  });
});