New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this.timeout() fails when using ES6's arrow functions #2018

Closed
ibc opened this Issue Dec 21, 2015 · 57 comments

Comments

Projects
None yet
@ibc
Copy link

ibc commented Dec 21, 2015

When using Node >= 4 with "use strict" and ES6 syntax for arrow functions, mocha fails:

describe('foo', () => {
  this.timeout(100);
});

# => TypeError: this.timeout is not a function

Using ES5 syntax does work:

describe('foo', function() {
  this.timeout(100);
});

So, which kind of ugly trick does mocha with this?

@danielstjules

This comment has been minimized.

Copy link
Contributor

danielstjules commented Dec 21, 2015

It binds the function to the test context, which can't be done when using arrow functions. From http://mochajs.org/

screen shot 2015-12-21 at 8 06 34 am

Sorry about that!

@ibc

This comment has been minimized.

Copy link

ibc commented Dec 21, 2015

Thanks.

Why so many "magic" that, at the end, produces problems? Why not this?:

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', () => ... );  
});

No globals, no problematic magic... but just JavaScript.

@danielstjules

This comment has been minimized.

Copy link
Contributor

danielstjules commented Dec 21, 2015

What you're proposing is a major breaking change and something that's being discussed in #1969 (comment) A rewrite might introduce those types of semantics :)

@ibc

This comment has been minimized.

Copy link

ibc commented Dec 21, 2015

Nice to know. Thanks.

@ORESoftware

This comment has been minimized.

Copy link

ORESoftware commented Jan 3, 2016

@ibc well, it would have to be

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', (suite) => ... );  
});

but are the two different suite arguments the same type? probably not, so then you have different variable names to reflect the different types like so

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', (test) => ... );  
});
@boneskull

This comment has been minimized.

Copy link
Member

boneskull commented Jan 3, 2016

anyway, the arrow operator cannot be used in functions that expect contexts.

I don't envision breaking the BDD UI for #1969--right off the bat, anyway--though I could be persuaded. It was my hope that we keep the existing API, and introduce a separate package containing the BDD UI. Mocha will ship with a version of the BDD UI which uses the existing API, but then we can release a new version of the BDD UI package using lambdas thereafter--users can choose whether or not to explicitly upgrade to that package.

@leonyu

This comment has been minimized.

Copy link

leonyu commented Mar 2, 2016

Maybe alternative ES6 syntax for describe or suite wrapper might solve this problem:

describe({ feature: 'create stuff' , do () {
    it('abc', () => {
    }); 
}})

This would at least allow binding at the suite level.

@mroien

This comment has been minimized.

Copy link

mroien commented Apr 20, 2016

Any update on this? I have this

mocha = require('mocha');

mocha.describe('test', (suite) => {

suite.timeout(500);

suite.it('test', (done)=>
          )
    }
)

And getting TypeError: Cannot read property 'timeout' of undefined

@boneskull

This comment has been minimized.

Copy link
Member

boneskull commented Apr 23, 2016

@mroien this is not a Mocha bug. the arrow syntax is not a 1:1 replacement for function. please read up on its limitations

DmitryDorofeev pushed a commit to DmitryDorofeev/mocha that referenced this issue Jul 1, 2016

neilbmclaughlin pushed a commit to nhsuk/connecting-to-services that referenced this issue Sep 27, 2016

neil mclaughlin
Extended timeout for problematic test
Needed to change function from arrow function as they are not
supported for mocha tests which require the use of timeout()

See: mochajs/mocha#2018

neilbmclaughlin pushed a commit to nhsuk/connecting-to-services that referenced this issue Sep 27, 2016

neil mclaughlin
Extended timeout for problematic test
Needed to change function from arrow function as they are not
supported for mocha tests which require the use of timeout()

See: mochajs/mocha#2018

neilbmclaughlin pushed a commit to nhsuk/connecting-to-services that referenced this issue Sep 27, 2016

neil mclaughlin
Extended timeout for problematic test
Needed to change function from arrow function as they are not
supported for mocha tests which require the use of timeout()

See: mochajs/mocha#2018

neilbmclaughlin pushed a commit to nhsuk/connecting-to-services that referenced this issue Sep 27, 2016

neil mclaughlin
Extended timeout for problematic test
Needed to change function from arrow function as they are not
supported for mocha tests which require the use of timeout()

See: mochajs/mocha#2018

neilbmclaughlin pushed a commit to nhsuk/connecting-to-services that referenced this issue Sep 27, 2016

neil mclaughlin
Extended timeout for problematic test
Needed to change function from arrow function as they are not
supported for mocha tests which require the use of timeout()

See: mochajs/mocha#2018

neilbmclaughlin pushed a commit to nhsuk/connecting-to-services that referenced this issue Sep 27, 2016

neil mclaughlin
Extended timeout for problematic test
Needed to change function from arrow function as they are not
supported for mocha tests which require the use of timeout()

See: mochajs/mocha#2018
@SamStonehouse

This comment has been minimized.

Copy link

SamStonehouse commented Nov 21, 2016

Did anything come of this? I like the proposed solution, if only for my love of arrow functions and aversion to 'this' when it's not required

@Munter Munter added the faq label Nov 22, 2016

@nomilous

This comment has been minimized.

Copy link

nomilous commented Dec 19, 2016

Since timeout is only relevant with done, why not simply attach the timeout function to the done function.

it('makes sense', done => {
    done.timeout(100);
});
@wmahad

This comment has been minimized.

Copy link

wmahad commented Dec 19, 2016

@nomilous this still doesn't work. I've had a similar problem. What works for my case is calling setTimeout inside the it block. e.g.

it('description', done => {
     const func = () => {
          // assertions
     };
     setTimeout(func, 10000);
});
@teameh

This comment has been minimized.

Copy link

teameh commented Dec 27, 2016

@nomilous synchronous or cases returning a promise can also have a timeout.

@andela-engmkwalusimbi this isn't supposed to work. As @boneskull wrote:

@mroien this is not a Mocha bug. the arrow syntax is not a 1:1 replacement for function. please read up on its limitations

@boneskull

This comment has been minimized.

Copy link
Member

boneskull commented Dec 28, 2016

For everyone still wondering about this, ensure you understand what an arrow function implies, then come back here and read on (there are plenty of resources out there that can explain this far better than I can).

The only way this arrow functions would work in this case is if we change the bdd API to pass a context object to every Runnable's (hooks, tests) callback, instead of leveraging this. That's not a bad idea, but it's an earthquake of a breaking change, so will never happen. Instead of this:

it('should do something', function (done) {
  this.timeout(9000);
  // stuff
  done();
});

it'd look like this:

it('should do something', (context, done) => {
  context.timeout(9000);
  done();
});

That'd would break every async Mocha test in existence, regardless if it was backwards-compatible otherwise:

it('should do something', function (context, done) {
  // context is unused, but 'done' is now the second parameter
  this.timeout(9000);
  done();
});

We could provide an alternative bdd implementation that does this, however--it just wouldn't be the default.

That's about the most thorough explanation I have of "where this issue is at". 😄

@SamStonehouse

This comment has been minimized.

Copy link

SamStonehouse commented Dec 28, 2016

Maybe it could be taken into consideration for the next major version? I don't think it's an important enough change to create an alternative bdd implementation. Having named arguments might help with future development too and maybe create a simple way to add some kind of test middleware like so:

it('should do something', function ({ context, done }) { ...

@danielstjules

This comment has been minimized.

Copy link
Contributor

danielstjules commented Dec 28, 2016

We could provide an alternative bdd implementation that does this, however--it just wouldn't be the default.

@boneskull A new bdd-es6 interface would be great :)

@steebchen

This comment has been minimized.

Copy link

steebchen commented Jan 3, 2017

Although I am in love with arrow functions which are really useful for e.g. Array functions like .filter(i => i.val), what's the problem with using normal functions? I think it's quite useful to have describe and it globally so I don't have to require them each time. Also since when is this magic, only because you don't understand (arrow) functions? I definitely don't want to provide a variable each time when I can return promises, otherwise I would have switched to something like ava a long time ago. Regarding the simplicity of mocha I think there shouldn't be any large change on normal/arrow functions described in #1969. And please don't tell me arrow functions are faster to type since your editor can transform a single f into function () {\n\t\n}.

@chovy

This comment has been minimized.

Copy link

chovy commented Jan 26, 2017

i'm unclear, is there a solution to timeout out a before() call that uses arrow functions?

    before( async function () {
      data = await provider.getData();
      console.log(data);
      this.timeout(30*1000);
    });

Has no effect. still getting 4 second timeout here. This is the only slow thing in my test suite. I can put timeout to 30s in mocha.opts to solve the problem, but I don't really need all tests to timeout after 30s just the one api call when 4s is fine for 99% of them.

Here's how I solved it in the meantime (note that the first describe() uses function instead of fat arrow syntax:

describe('Search API', function () {
	this.timeout(30*1000);

	context('search', () => {
		let data;

		before( async () => {
		  data = await provider.getSearch();
		});

		it('returns results', () => {
		  expect(data).to.exist;
		  expect(data.results).to.be.instanceOf(Array);
		  expect(data.results.length).to.be.above(0);
		});
	})
});
@ScottFreeCode

This comment has been minimized.

Copy link
Member

ScottFreeCode commented Aug 9, 2017

I don't think that should mean we throw up our hands and say its all syntax, and lets just use function/return.

I like arrows, and you like functions.

...it had better be more thought out than knee-jerks like: ... "you can still do fp with a more verbose syntax".

That's pretty much the opposite of what I said, though -- I was going for the fact that arrow functions that still use this wouldn't be FP in the first place, they'd be OO with arrow functions (the inverse of FP with traditional JS functions). In other words, far from it being just a different syntax, the real problem is that there's a deeper paradigm incompatibility than just the syntax incompatibility (as Mocha is currently designed anyway).

I'm fairly sure it's possible to build an alternative interface on top of Mocha to replace this altogether with parameters. I just want to make it clear that if you want to go FP in writing tests you're going to have to do more than just come up with a way to pass Mocha's this to arrow functions. I'm very much for rising to that sort of challenge, however. ;^)

(There are also several other behaviors of Mocha that are stateful, global, or -- worse -- both, but I don't have time at the moment to come up with a brief way to list them. If you've ever seen the issues surrounding running Mocha more than once, that's an example of them, though.)

At present, is there any other way to declare a custom timeout per test than this.timeout?

Unfortunately, I'm fairly sure there isn't; off the top of my head the suggestion to accept an additional parameter to it that would be a key-value map (as a JS object) of configuration settings sounds like a decent future solution in Mocha, if anyone wants to try implementing it.

@astitt-ripple

This comment has been minimized.

Copy link

astitt-ripple commented Aug 15, 2017

At present, is there any other way to declare a custom timeout per test than this.timeout?

Unfortunately, I'm fairly sure there isn't;

Thank you for confirming this detail. This solidifies the point I was making.

off the top of my head the suggestion to accept an additional parameter to it that would be a key-value map (as a JS object) of configuration settings sounds like a decent future solution in Mocha, if anyone wants to try implementing it.

+1 for a more general solution.

From what I can tell though, it seems workable to pass a single parameter still. Combine this with the done callback (so this becomes a function). Then have mocha run each test wrapped with a promise (well, two actually, one for handling the timeout, and one for actually running the test), regardless of the parameter count (departure from how it works today). It could then check that the result of the function was a promise or not. If not, call done after the synchronous function returns to finalize the test. If the result of the function is instead a promise, then wait for it to resolve (or reject). If the timeout occurs, stop the test (same as before). In the case where a test manages to call done and also return a promise. Either done is called before resolution, in which case mocha should wait for the promise and then fail the test for having an ambiguous completion sequence. Or done is called sometime after resolution, in which case somehow the test must be retroactively failed -- or the issue signaled some other reasonable way. Sorry thats a lot of broad strokes, but thats my outsider's understanding of what mocha is trying to do and the quirks its running into. What other considerations might prevent this from being a workable solution?

if you want to go FP in writing tests you're going to have to do more than just come up with a way to pass Mocha's this to arrow functions

agreed. there is a definite change in going to a different computational model, and in addition javascript is a complex ecosystem with much to consider. In my specific use case, however setting the timeout from time to time (such as to be more accurate based on some computation instead of a fixed default value), is the only tangible issue I've encountered writing FP tests with Mocha (so far at least). Which is great. 👍

That said, I'd like to know what else you see as impending obstacles that specifically have to do with Mocha (as opposed to what writing tests in FP might mean in general).

That's pretty much the opposite of what I said, though

I am sorry if I mischaracterized or misunderstood. For a lot of what I wrote, I'm not sure I was getting across as intended either, based on the replies. Which is unfortunate, since I think if we got down to it, we would probably come to pretty close agreement on what FP should look like in theory. Here it seems however, we disagree as to what a workable reduction to practice might look like in the currently available releases of Mocha today, at least for some users/use-cases. So I'm not quite sure exactly what the major issue is with the proposed add-on function I proposed, from your side.

@ScottFreeCode

This comment has been minimized.

Copy link
Member

ScottFreeCode commented Aug 24, 2017

(Quoted and replied in order, but arguably the more important stuff is later rather than earlier.)


From what I can tell though, it seems workable to pass a single parameter still. Combine this with the done callback (so this becomes a function). Then... What other considerations might prevent this from being a workable solution?

If we break backwards-compatibility, there are way simpler designs we could switch to.

If we keep backwards compatibility, both of these tests need to pass and not time out:

it("runs immediately", () => {
  // call functions and assert whatever
})
it("runs asynchronously", doneWithSomeOtherName => {
  setTimeout(() => {
    // call functions and assert whatever
    doneWithSomeOtherName()
  }, 100)
})

You're welcome to try to come up with some example code to prove otherwise (although I'd suggest focusing on the suggestion at the end of this comment instead), but I'm fairly sure no design will be able to do that and also make this test pass and not time out:

it("looks just like an asynchronous test with a different name for `done`, but never tells Mocha it's done", context => {
  context.configureByMutation("some value")
  // call functions and assert whatever
})

But also, notice the mutation there. More on this below.


In my specific use case, however setting the timeout from time to time (such as to be more accurate based on some computation instead of a fixed default value), is the only tangible issue I've encountered writing FP tests with Mocha (so far at least). Which is great.

👍!


That said, I'd like to know what else you see as impending obstacles that specifically have to do with Mocha (as opposed to what writing tests in FP might mean in general).

I think I may not have been communicating this quite focus...edly, so let me see if I can boil it down a bit further. (Also sorry if any of this has come off as antagonistic; it certainly isn't intended to be, although I'll admit to trying to change mindsets here.) Mocha's codebase is very much of the class hierarchies and getters and setters style of "object orientation" (and there are multiple issues or potential issues Mocha has that I believe come down to its mutable state), but that doesn't generally affect your test code if all you're doing is writing tests and letting Mocha run them. You can do weird things with Mocha's imperative mutating configuration:

it("imperatively sets the timeout multiple times", function(done) {
  this.timeout(5000)
  var context = this
  setTimeout(function() {
    context.timeout(1000)
    setTimeout(done, 500)
  }, 4000)
})

...but you don't have to. As with so many elements of programming functionally in non-functional languages: just don't abuse the imperative stuff.

(There's also an argument to be made that exceptions are impure, but I'm not yet convinced that's true of exceptions that are thrown based on the input -- which could just be considered another form of output. So some would say that using assertions that throw is not functional, but I'm not going to tackle that at this point.)

(Important part here:) What I'm trying to get at is, we're looking at potentially either adding complication to an already complex codebase, or else making a backwards-incompatible change. We need justification for either of those things. If the justification is "make tests more functional", that's good (in my book anyway). A design that makes tests more functional might be worth the trouble (depending on how much trouble it is). But if by "make tests more functional" you mean "make arrow functions mutate things", i.e. "make arrow functions less functional", that weakens the case a lot (if it isn't just self-contradictory). More completely: I don't think that making tests look more functional (as clean as arrow functions do look!) while retaining the one bit of mutation involved, however small that bit was to begin with, is nearly as compelling a justification as actually getting rid of that bit of mutation would be -- at least if the point is to make tests more functional.

I probably shouldn't even have got so far off on this tangent though; see below concerning solutions. 😸


So I'm not quite sure exactly what the major issue is with the proposed add-on function I proposed, from your side.

(Also important part here:) Well I like the bit where it takes the timeout as a parameter instead of a method call, actually! If you can come up with a way to generalize that to the rest of Mocha's configuration methods (there are a lot of them -- and some apply to synchronous tests if I recall correct, hence why we can't just add the same methods as properties on done and let people write asynchronous tests that can call configuration through done, but I digress), then I'd definitely want to take a look at it. At the very least we may want to make it a recommendation, and we might even be able to adopt the implementation into it when passed a third parameter (or something like that, maybe it.configured or it(...).configured(...) if we don't want more number-of-parameters shenanigans...) -- which I think would be a backwards-compatible solution that tackles the underlying mutation/imperative matter and gets arrow function support "the right way" (what I'm arguing is such anyway): because it fits the new behavior. I guess what I should have said, rather than going after this in the workaround, is: let's expand the parameter part of it!

@thom-nic

This comment has been minimized.

Copy link

thom-nic commented Sep 29, 2017

I swear I read somewhere you could do something like this:

describe('a slow thing', () => {
 // test code...
}).timeout(5000);

which doesn't alter the parameter contract to the provided function. Now I can't find any reference to any such thing so maybe I just imagined it.

@cornpops

This comment has been minimized.

Copy link

cornpops commented Nov 12, 2017

@thom-nic this works! i guess it makes sense since all of mocha's functions returns its context

return this;

@amado-saladino

This comment has been minimized.

Copy link

amado-saladino commented Nov 19, 2017

It works with me when replacing the arrow function form with the normal function

function() { ..... }

@astitt-ripple

This comment has been minimized.

Copy link

astitt-ripple commented Nov 21, 2017

Hi Folks. I apologize for going dark after sparking the discussion back in august, i've been quite busy and actually have mostly completed/moved-on from that work.

I appreciate the detailed response about the different use cases and how its hard to dovetail them together. That was the most concise showing of the different setups mocha has to support that I've read. So thank you for your time on that one.

Looking back, its clear that I must have over-emphasized getting access to mocha context (this), when that aspect was really more of a convenient after-thought. I didn't realize how easily it would draw attention away from what I was actually trying to do: which was having some fun by adding a temporal'ish extension (when) to the test dsl for streamlining one-off timeout adjustments (plus eliminating a common error for a particular style of tests, which I'll explain below). Returning this was just another fun thing I thought of adding, the main idea (hence the name when) was for handling cases that needed different timeouts than normal.

Obviously, if I wanted to access the bound context I could simply use function directly as many have suggested, rather than hoisting it out with a wrapper. Thats not the issue. 😄 It isn't lost on me how that might seem strange on the face of it. I'm hoping maybe it will round out the picture if I show how I was setting up some of the tests which led me down this path to begin with. To be clear, I'm not trying to sell any particular style here, use what works for you. This is what worked for me.

Ok
First off, start with the assumption that we're testing some setup that basically does one thing, but will do it for a wide range of inputs, and so we must test this thing out in a bunch of scenarios to ensure the outputs are correct. However, since the relevant application code "does one thing", the underlying test procedure is pretty much always the same. I also don't want to duplicate/mutate the test body needlessly, since that slows down how quickly I can add more test cases for new inputs and eventually the maintenance would become unreasonable.

So instead we write a function thats general enough to start the application code up with any potentially supported inputs, perform the test action, and then assert the results... Add that in my case I was working with the Promise abstraction (for reasons that i'll leave out), so this generalized test procedure function naturally has to return that promise.then chain. Bread and butter es6 kind of stuff, so far so good.

Now comes the test scenarios, since we packed everything into our test-procedure function, the test cases are effectively defining the inputs and invoking the function.

So perhaps I write a bunch of tests like this, and everything seems to be working:

it('does something', function() {
  testProcedureFunction('something','1')
})

If you're following along closely, you will have probably noticed already that this example has a bug. It is missing its return, and since testProcedureFunction is built on promises (pun intended), its always going to pass no matter whether the assertions at the end passes or fails. This is a bug, and it can be an extremely subtle one to track down sometimes. To illustrate, depending on how we wrote testProcedureFunction and how the application is written, lets say there is some synchronous code at the start, and that blows up instead of the end-of-test assertions, the testcase might even fail -- leading us to think everything is fine.

The test should of course really look like this with a return:

it('does something', function() {
  return testProcedureFunction('something','1')
})

Now I know this test is going to be one line in most cases. Actually, every case will be one line, except for when the inputs are such that a larger timeout is required. Now among the differences between classical js functions and arrows, there is a particular aspect of arrow functions that is useful here: a single statement arrow function has an implied return when braces are omitted. If instead of writing a function {...}, a test uses the => ..., then I can easily scan those cases for the arrow and the lack of curly braces, and quickly infer that they cannot have this missing return issue, and it will take some extra steps (adding the braces back) for someone to mess that up.

Like so:

it('does something', () => testProcedureFunction('something','1'))

Now what if one of these cases takes longer than the others! We can of course set the timeout like this:

it('does something slow', function() {
  this.timeout(10000)
  return testProcedureFunction('somethingSlow','2')
})

Or maybe someone will make a mistake and do this first (which doesn't work of course):

it('does something slow', () => {
  this.timeout(10000)
  return testProcedureFunction('somethingSlow','2')
})

But now we're back where we started, the codebase has a pattern ripe for repeating, which is susceptible to the missing return bug (either by future me, or the next person to add a feature -- the point is, its an easy mistake to make, it may go unnoticed, and it can be hard to track down). The when function solves this, and lets us use arrows consistently again:

it('does something slow', when(() => testProcedureFunction('somethingSlow','2'), 10000))

(note, i wasn't able to get the .timeout(5000) dot-chaining suggestion above to work, may have been due to the version of mocha i needed to use, I don't remember anymore, will give that a try!)
(note2, notice that the use of when isn't using the this parameter hoisting trick -- it really was just an after-thought).

Maybe there are linters that can flag missing returns-for-promise bugs (or probably more realistically, enforcing a return statement with a rhs for every function). However that wasn't an option at the time, plus I think the arrow syntax comes out shorter and I find it (subjectively/personally) easier to read and work with, which tipped the scales for me away from function.

So there you have it.

I don't know if I'll have time to respond again anytime soon, so I hope that was at least informative and clear, and maybe even puts some of the controversy surrounding the whole "access to mocha-context from arrows" stuff to bed.

Lastly, since I found function vs => confusing for a long time, I'll drop this link in case its not clear to anyone casually reading why arrows can't access this. It was the clearest explanation of functions vs arrows that I've found, and was what helped me finally understand the differences well enough to use them with full confidence.

https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/

@aleung

This comment has been minimized.

Copy link

aleung commented Dec 20, 2017

@thom-nic It works on it, but not on describe.

describe('my test suite', () => {

  it('test case 1', () => {
    // ...
  }).timeout('10s');  // Works on `it`. This test case doesn't timeout

  it('test case 2', () => {
    // ...
  });  // This test case timeouts.

}).timeout('10s');  // Doesn't work on `describe`. Tests are already created when runs to here.
@amado-saladino

This comment has been minimized.

Copy link

amado-saladino commented Dec 21, 2017

@thom-nic , you may use the normal function form

describe('my test suite', function() {
this.timeout(n);

...
}
@Jakobud

This comment has been minimized.

Copy link

Jakobud commented Apr 20, 2018

Anyone complaining about this doesn't understand arrow functions.

Arrow functions are NOT a new fancy ES6 thing is supposed to replace the classic function () {}. The only purpose of arrow functions is that it inherits this from it's parent, where the classic function () has it's own this.

Yes, even when using full ES6 syntax, you should still be using function () if you want to use this in the correct context of your function. You should be using both function () and () => in your ES6 application depending on what you are trying to do.

this.timeout() doesn't work with it('....', () => { ... }) because the callback is inheriting this from the parent describe() function, in which this.timeout() doesn't make sense on that level.

@Flamenco

This comment has been minimized.

Copy link

Flamenco commented Apr 20, 2018

Don't lambda functions also allow you to automatically send a single argument to the function without declaring it in the call?

(param)=>aFunction
...then(aFunction)

function(){} can be bound to 'this' but ()=> is 'locked in'

Arrow functions should replace the traditional function when the receiver expects a pre-determined 'this' in the same context that it is called from, (and also benefit from typing less code).

I would go so far as to say never use function() unless you want 'this' to be something other than what 'this' is when invoking it.

@codefoster

This comment has been minimized.

Copy link

codefoster commented Apr 21, 2018

@Flamenco...

Don't lambda functions also allow you to automatically send a single argument to the function without declaring it in the call?

I'm not sure I understand exactly how you're putting that.
As far as "sending arguments to the function", fat arrows work just like regular functions with a single exception: if you have exactly 1 argument, then you can leave off the parenthesis.

() => console.log("hi"); //zero arguments requires empty parenthesis
a => console.log(a); //you can optionally leave the parenthesis off for 1 argument
(a,b) => console.log(`${a} ${b}`); //2..n arguments requires parenthesis

What you may have been getting at is that fat arrows allow you to return a value by omitting the curly braces and the return keyword as long as your function is a single expression.
So if you had...

setTimeout(function(a,b) { doSomething(); return calculateSomething(a,b); }, 5000);

...and you wanted to convert that into a fat arrow function, you would not be able to shake the curly braces and the return keyword because the function body has multiple statements. You'd do it like this...

setTimeout((a,b) => { doSomething(); return calculateSomething(a,b); }, 5000);

If, rather you started with...

setTimeout(function(a,b) { return calculateSomething(a,b); }, 5000);

...then you're dealing with a function that so simple that it just returns a single expression and you could use...

setTimeout((a,b) => calculateSomething(a,b), 5000);

That got a lot easier to read!
I wrote a bit more about this at codefoster.com/levelup-arrays.

@leonyu

This comment has been minimized.

Copy link

leonyu commented Apr 21, 2018

There are many different coding styles in JavaScript -- from OOP to FP, from strict type-safety to mixin/duck-typing. In addition, there are advanced patterns in each of those styles (i.e. dependency injection in OOP camp, currying/monad in the FP camp).

If your coding style is closer to FP where this is not used and arrow functions are used to reduce boilerplate, having to preserve this is an extra overhead for any advanced testing (e.g. parameterized testing, creating DSL).

Any experienced developer can just pre-wrap the testing framework to suit their coding style, but that means the framework is less "out-of-the-box". That translates to extra work for upgrading, adopting plugins, and onboarding new engineers.

@boneskull

This comment has been minimized.

Copy link
Member

boneskull commented Apr 21, 2018

I like the idea of an alternative bdd interface which doesn’t use this and instead passes what would normally be the context object as a parameter to describe, it and hooks.

But it’s not so straightforward to implement, IIRC. Would be cool to see an attempt though.

@RobRendell

This comment has been minimized.

Copy link

RobRendell commented Aug 27, 2018

I know this is getting into serious side-effects land, but couldn't you deal with the done-or-context parameter something like this?

it("runs immediately", () => {
  // call functions and assert whatever
})
it("runs asynchronously", doneOrContext => {
  setTimeout(() => {
    // call functions and assert whatever
    doneOrContext();
  }, 100)
})
it("runs immediately using the parameter as a context", doneOrContext => {
  doneOrContext.configureByMutation("some value");
  // As well as changing config, also flags to Mocha that this test is treating the
  // parameter as a context object and is therefore not async.
  // Call functions and assert whatever
})
it("runs asynchronously using the parameter as a context", doneOrContext => {
  doneOrContext.configureByMutation("some value");
  doneOrContext.setAsync(); // Flags to Mocha that even though the parameter has been used as
  // a context object, the test is in fact asynchronous.
  setTimeout(() => {
    // call functions and assert whatever
    doneOrContext();
    // or doneOrContext.done()
  }, 100)
})
@selvakumar1994

This comment has been minimized.

Copy link

selvakumar1994 commented Aug 29, 2018

i used the below script but i got the same Timeout exceed error.

Myscript :

describe("getBillingDetail", async function (){
this.timeout(55000);
it.only("check given valid Jobname",async function (done){
this.timeout(55000);
var result = await url.getBillingDetail('12254785565647858');
console.log(result);
assert.equal(result,true);
});
});

Error: Timeout of 55000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

@Munter

This comment has been minimized.

Copy link
Member

Munter commented Aug 29, 2018

Don't pass a done callback to an async function

@papercuptech

This comment has been minimized.

Copy link

papercuptech commented Sep 21, 2018

I've created a prototype solution that's backwards compatible. For now it's a separate module, but functionality could easily be merged into mocha proper.

https://github.com/papercuptech/mocha-lambda

quick way

require('mocha-lambda')
// now a global '_tst' can be used where 'this' was before

describe('suite', () => {
  beforeEach(() => {
    _tst.answer = 42
  })

  it('provides "this" as "_tst"', function() {
    assert(this === _tst)
  })

  it('works', () => {
    assert(_tst.answer === 42)
    _tst.skip()
  })
})

for explicit naming (and works with TypeScript)

// if you were previously explicitly importing api (describe, it, etc.) from 'mocha',
// you will have to change to importing from 'mocha-lambda', until (or if) this
// gets merged into mocha proper
import ctx, {describe as d, it as i} from 'mocha-lambda'

d('suite', () => {
  // ctx() is a function that returns "this" as appropriate
  i('works using ctx()', () => {
    ctx().skip()
  })
})

import {Context} from 'mocha'
// ctx() can also rename global
ctx('t')
declare var t: Context
d('suite', () => {
  // ctx() is a function that returns "this" as appropriate
  i('works using renamed global', () => {
    t.skip()
  })
})
@aleung

This comment has been minimized.

Copy link

aleung commented Sep 21, 2018

@papercuptech The link is 404 not found.

@papercuptech

This comment has been minimized.

Copy link

papercuptech commented Sep 21, 2018

Woops.. was private repo. now public

Can also npm i mocha-lambda

@papercuptech papercuptech referenced this issue Sep 24, 2018

Closed

Enhanced control of test context #3485

4 of 4 tasks complete
@papercuptech

This comment has been minimized.

Copy link

papercuptech commented Sep 24, 2018

@aleung @linesh-simplicity, this is superseded by #3485

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment