jest-extended icon indicating copy to clipboard operation
jest-extended copied to clipboard

New matcher: toBeNear(value, range)

Open JShabtai opened this issue 7 years ago • 16 comments

Feature Request

Description: It would be nice to have a way of checking that a value is "close enough" to the expected result (e.g. due to roundoff errors). Using toBeWithin() can accomplish this, but it's a fairly verbose way of doing it. Possible solution: Should be equivalent to .toBeWithin(value - range, value + range)

JShabtai avatar Jul 06 '18 21:07 JShabtai

so something like this?

I feel this is a pretty simple thing to have, maybe have a default of range 1 when its not passed.

describe('test examples', () => {
  it('toBeWithin simple', () => {
    const example = 1.25
    expect(Math.round(example)).toBeWithin(1, 2)
  })
  it('toBeWithin complex', () => {
    const example = 1.25
    const range = 1
    expect(Math.round(example)).toBeWithin(example - range, example + range)
  })

  it('toBeNear with 1 arg as toBeWithin', () => {
    const example = 1.25
    const range = 2
    expect(Math.round(example)).toBeWithin(example - 1, example + 1)
    // *** The toBeNear alternative ***
    // expect(Math.round(example)).toBeNear(example)
    // returns true as it + or - 1 from the input.
  })

  it('toBeNear with 2 arg as toBeWithin', () => {
    const example = 1.25
    const range = 2
    expect(Math.round(example)).toBeWithin(example - range, example + range)
    // *** The toBeNear alternative ***
    // expect(Math.round(example)).toBeNear(example, range)
  })
})

benjaminkay93 avatar Jul 08 '18 13:07 benjaminkay93

so in short, these should all pass?

it('toBeNear', () => {
    expect(10.31415).toBeNear(10)
    expect(10.31415).toBeNear(10, 2)
    expect(10.31415).not.toBeNear(10, 0.3)
  })

benjaminkay93 avatar Jul 08 '18 13:07 benjaminkay93

Yeah, that's pretty much what I had in mind. I'm not sure about the default value though. 1 might make sense in some cases, but be an absurdly large (or small) range in others.

Also consider that for large values, a default range of 1 is effectively useless since (with floating point numbers) adding 1 might have no effect

> (1e99 + 1) == 1e99
true

I think it would make more sense for the second argument to be required, so the developer is forced to pick something that makes sense in context.

JShabtai avatar Jul 08 '18 17:07 JShabtai

fair, that that makes sense. I would like to see this myself, but i don't know what others would think

benjaminkay93 avatar Jul 08 '18 23:07 benjaminkay93

Hey @JShabtai @benjaminkay93 I like this matcher, nice work! I agree that the second range argument should be required.

Do either of you fancy sending a PR for this?

mattphillips avatar Jul 30 '18 14:07 mattphillips

I could give it a go, but I'm not sure when I'll have time. If nobody else wants to do it, I'll get around to it eventually, but that might be a while.

JShabtai avatar Jul 30 '18 14:07 JShabtai

Looking at the toBeCloseTo matcher built into expect, I'm wondering if having two similar functions with similar names might cause too much confusion?

Frankly, I don't like that function, since it takes the number of decimal places to consider, rather than just a range (you may want to use this for large numbers with nothing after the decimal place, or ranges like 0.004 instead of 0.01 or 0.001), but having confusing names can potentially cause more problems than it solves. Thoughts?

JShabtai avatar Aug 03 '18 23:08 JShabtai

Same with: https://github.com/jest-community/jest-extended/issues/126

hustcc avatar Aug 31 '18 01:08 hustcc

This seems simple enough. I'll give this a crack over the coming weekend. Are we agreed on just having both arguments be required then?

natealcedo avatar Oct 09 '18 13:10 natealcedo

What is the situation with this / is any help needed? This would be great to have!

marharyta avatar Oct 15 '18 21:10 marharyta

@natealcedo have you started on this yet? If not @marharyta your help will be greatly appreciated :)

mattphillips avatar Oct 15 '18 21:10 mattphillips

hey @marharyta ! Please go ahead. I was gonna do it over the weekend but I had other commitments. Just shoot if you need any help. :)

natealcedo avatar Oct 16 '18 01:10 natealcedo

I'd like to implement this. Should tests pass when the received value is equal to value plus or minus range?

// should these tests pass or fail
test('toBeNear', () => {
  expect(1).toBeNear(2, 1);
  expect(3).toBeNear(2, 1);
});

Also, I think naming the second argument epsilon would make it slightly more clear. Any thoughts on that?

phapp88 avatar Nov 08 '18 15:11 phapp88

@phapp88 I don't think it should matter too much whether the bounds match, but I would lean towards saying the tests you posted should pass. This use case is already for fairly fuzzy tests, having one more passing value shouldn't be a problem.

I'm not sure if epsilon is a better name. I think that's really only going to be clear to people with a background in math/science. Maybe 'marginOfError', 'offset' or 'radius'?

JShabtai avatar Nov 08 '18 15:11 JShabtai

@JShabtai Yeah, you make a good point that epsilon might not be clear for some people. I think 'offset' works pretty well.

phapp88 avatar Nov 08 '18 16:11 phapp88

@brian-lives-outdoors has given a solution on SO, which I've been using successfully for the past several months.

dandv avatar Feb 24 '19 00:02 dandv

Is it still relevant ? Maybe this can be either closed for housekeeping purpose, or I could open a new PR for this.

GerkinDev avatar Aug 29 '22 13:08 GerkinDev

I think closing it makes the most sense. There are some reasonable alternatives in the comments above.

JShabtai avatar Aug 29 '22 17:08 JShabtai