Unit Testing Show of Hands

86 views
Skip to first unread message

Kris Kowal

unread,
Sep 14, 2009, 4:15:50 PM9/14/09
to comm...@googlegroups.com
We have a couple proposals perched on the edge of the ratification
cliff, so in the interest of picking some low-hanging fruit and making
some progress, I've shined up Unit Testing A for a show of hands.

http://wiki.commonjs.org/wiki/Unit_Testing/A

As a reminder, I incorporated our consensus on the argument order and
default messages. I've also opted to simplify the API, removing some
functions that seem convenient but are actually superfluous. The
idioms for isTrue, isFalse, eqTrue, isNaN, isntNan, &c, all boil down
to:

assert.is(true, actual, message);
assert.is(false, …);
assert.is(false, …);
assert.eq(true, …); // truthy
assert.is(NaN, …);
assert.ni(NaN, …);

So, the functions I've opted to include were "eq", "ne", "is", "ni"
(which is new, but also funny, which I think makes it easier to
remember, and analogous to "ne" in at least a couple ways). I also
left a note that, yes, eventually it would be really cool to be able
to send test results to an object implementing a standard logging
interface. However, to get this out the door, I think we can safely
defer that to a future discussion and a version 2 spec.

Is this something we can ratify and start using? Once this is real,
we can start standardizing unit tests in our specification proposals.
Please show your hand on the Wiki.

http://wiki.commonjs.org/wiki/Unit_Testing/A

Kris Kowal

Hannes Wallnoefer

unread,
Sep 14, 2009, 5:04:11 PM9/14/09
to comm...@googlegroups.com
2009/9/14 Kris Kowal <cowber...@gmail.com>:
>
> We have a couple proposals perched on the edge of the ratification
> cliff, so in the interest of picking some low-hanging fruit and making
> some progress, I've shined up Unit Testing A for a show of hands.
>
> http://wiki.commonjs.org/wiki/Unit_Testing/A
>
> As a reminder, I incorporated our consensus on the argument order and
> default messages.  I've also opted to simplify the API, removing some
> functions that seem convenient but are actually superfluous.  The
> idioms for isTrue, isFalse, eqTrue, isNaN, isntNan, &c, all boil down
> to:
>
> assert.is(true, actual, message);
> assert.is(false, …);
> assert.is(false, …);
> assert.eq(true, …); // truthy
> assert.is(NaN, …);
> assert.ni(NaN, …);

I would prefer to keep it slightly more verbose. Especially, the "ni"
and "ne", while funny, may be confusing. Actually, I was planning to
write another unittest proposal, and I think I'm going to do that now,
so I'll cast my vote for "more discussion".

But yes, it would be great to have some a ratified spec soon, so I'm
all for a quick procedure. I'll have a pollable naming proposal ready
tomorrow morning.

Quickly, my main points of objection are:

* Don't use abbreviations in assertion methods unless necessary. I
prefer the slightly more verbose isEqual, isNotEqual, is, isNot to eq,
ne, is, ni.
* isTrue() and isFalse() are very useful assertion methods, as they
can be used to build more complex assertions. I think isNull() and
isUndefined() are also worth having.
* throws() might be more self-explanatory than error().

Hannes

Ash Berlin

unread,
Sep 14, 2009, 5:10:49 PM9/14/09
to comm...@googlegroups.com
On 14 Sep 2009, at 22:04, Hannes Wallnoefer wrote:

* throws() might be more self-explanatory than error().

throwsOk perhaps?

Daniel Friesen

unread,
Sep 14, 2009, 5:12:10 PM9/14/09
to comm...@googlegroups.com
IMHO the direction with all these methods adds a pile of unnecessary
cruft, and misses out on stuff that's actually useful.

When doing tests assert() and assert.not() has been enough to handle all
those cases listed there.
I don't see the point in obfuscating a few === or =='s (reading a method
isn't quite clear on that) which could be written out on their own
easily enough.

While writing tests for Wrench.js the big trouble that isn't even
covered here was arrays, which resulted in me creating:
assert.match(arr, ["foo", "bar", "baz"]);

So:
-1 To .eq, .ni, abbreviations, and all these superfluous methods
+1 To thought on methods like assert.match to deal with things you need
to test that aren't easy to do with reliable builtin methods (like
comparing arrays based on if their contents match).

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Ash Berlin

unread,
Sep 14, 2009, 5:18:10 PM9/14/09
to comm...@googlegroups.com

On 14 Sep 2009, at 22:12, Daniel Friesen wrote:

>
> IMHO the direction with all these methods adds a pile of unnecessary
> cruft, and misses out on stuff that's actually useful.
>
> When doing tests assert() and assert.not() has been enough to handle
> all
> those cases listed there.
> I don't see the point in obfuscating a few === or =='s (reading a
> method
> isn't quite clear on that) which could be written out on their own
> easily enough.
>
> While writing tests for Wrench.js the big trouble that isn't even
> covered here was arrays, which resulted in me creating:
> assert.match(arr, ["foo", "bar", "baz"]);
>
> So:
> -1 To .eq, .ni, abbreviations, and all these superfluous methods
> +1 To thought on methods like assert.match to deal with things you
> need
> to test that aren't easy to do with reliable builtin methods (like
> comparing arrays based on if their contents match).

Thats my votes too.

As always when testing comes up, I will pimp the function I found on
the internet to do just that:

http://philrathe.com/articles/equiv

Daniel Friesen

unread,
Sep 14, 2009, 5:37:16 PM9/14/09
to comm...@googlegroups.com
For reference, my tests:
http://github.com/dantman/wrench/tree/master/test/tests/

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Kris Kowal

unread,
Sep 14, 2009, 5:48:51 PM9/14/09
to comm...@googlegroups.com
On Mon, Sep 14, 2009 at 2:37 PM, Daniel Friesen
<nadir.s...@gmail.com> wrote:
> For reference, my tests:
> http://github.com/dantman/wrench/tree/master/test/tests/

Ah, yeah, here are Narwhal's:

http://github.com/tlrobinson/narwhal/tree/master/tests/

Kris Kowal

Kris Kowal

unread,
Sep 14, 2009, 5:56:29 PM9/14/09
to comm...@googlegroups.com
On Mon, Sep 14, 2009 at 2:12 PM, Daniel Friesen
<nadir.s...@gmail.com> wrote:
>
> IMHO the direction with all these methods adds a pile of unnecessary
> cruft, and misses out on stuff that's actually useful.
>
> When doing tests assert() and assert.not() has been enough to handle all
> those cases listed there.
> I don't see the point in obfuscating a few === or =='s (reading a method
> isn't quite clear on that) which could be written out on their own
> easily enough.
>
> While writing tests for Wrench.js the big trouble that isn't even
> covered here was arrays, which resulted in me creating:
> assert.match(arr, ["foo", "bar", "baz"]);
>
> So:
> -1 To .eq, .ni, abbreviations, and all these superfluous methods

Agreed on superfluous .isEqual, .isTrue, &c. They're the least worthy
kind of cruft. These are equivalent:

assert.is(true, /^.$/.test('a'), 'regexp dot matches char');
assert.isTrue(/^.$/.test('a'), 'regexp dot matches char');

That's almost no savings in readability or expressiveness, or even
brevity. It just burdens the API with an open-ended list of functions
named after constants that all ultimately forward to assert.is and
assert.eq.

As for assert.match, the specification for "assert.eq" does the job,
and can be backed by Ash's equiv.

assert.eq(["foo", "bar", "baz"], ["foo", "bar", "baz"]); // equal but
assert.ni(["foo", "bar", "baz"], ["foo", "bar", "baz"]); // not identical

Kris Kowal

Ash Berlin

unread,
Sep 14, 2009, 6:15:04 PM9/14/09
to comm...@googlegroups.com
Okay, so the only issue now is exactly which fns we do have on assert. I shall let Kris and Daniel fight out the naming. I would like to make one addition, and that is the companion to error:

assert.lives(block, message); // Expects block to not throw an error. 

Hmmm tho i guess thats not strictly needed ,as any error that gets throw should get reported nicely anyway. Okay so I might like lives just for balance sake, but feel free to say no and I will still be equally happy.

What ever the two of you decide between you naming wise i am happy with and gets my vote.

Neville Burnell

unread,
Sep 14, 2009, 6:25:59 PM9/14/09
to CommonJS

> I would prefer to keep it slightly more verbose.

+1

On Sep 15, 7:04 am, Hannes Wallnoefer <hann...@gmail.com> wrote:
> 2009/9/14 Kris Kowal <cowbertvon...@gmail.com>:

Hannes Wallnoefer

unread,
Sep 14, 2009, 7:29:50 PM9/14/09
to comm...@googlegroups.com
2009/9/14 Kris Kowal <cowber...@gmail.com>:
Here are Helma's. This includes all (or most) Narwhal serverjs tests
for the binary and file modules. Porting them to helma/unittest was
pretty straightforward.

http://github.com/hns/helma-ng/tree/master/modules/test/

Hannes

> Kris Kowal
>
> >
>

Tom Robinson

unread,
Sep 15, 2009, 12:38:19 AM9/15/09
to comm...@googlegroups.com

On Sep 14, 2009, at 2:12 PM, Daniel Friesen wrote:

>
> IMHO the direction with all these methods adds a pile of unnecessary
> cruft, and misses out on stuff that's actually useful.
>
> When doing tests assert() and assert.not() has been enough to handle
> all
> those cases listed there.
> I don't see the point in obfuscating a few === or =='s (reading a
> method
> isn't quite clear on that) which could be written out on their own
> easily enough.

I would at least include basic comparison functions, since it allows
the test harness to print what it expected and what it actually was,
which can't be done with just assert/assert.not.

is{True,False,Null,etc} can be left out though.

Hannes Wallnoefer

unread,
Sep 15, 2009, 4:06:24 AM9/15/09
to comm...@googlegroups.com
2009/9/15 Tom Robinson <tlrob...@gmail.com>:

>
> is{True,False,Null,etc} can be left out though.

assertTrue() is used 51 times in our tests, so there must be some use
for it for some people. (Among these are 25 uses in Narwhal's
bytearray/bytestring tests, btw.)

>>
>> While writing tests for Wrench.js the big trouble that isn't even
>> covered here was arrays, which resulted in me creating:
>> assert.match(arr, ["foo", "bar", "baz"]);

The equal assertion should do a deep comparison of arrays and return
true for arrays with the same contents.

Hannes

Tom Robinson

unread,
Sep 15, 2009, 6:38:28 AM9/15/09
to comm...@googlegroups.com

On Sep 15, 2009, at 1:06 AM, Hannes Wallnoefer wrote:

>
> 2009/9/15 Tom Robinson <tlrob...@gmail.com>:
>>
>> is{True,False,Null,etc} can be left out though.
>
> assertTrue() is used 51 times in our tests, so there must be some use
> for it for some people. (Among these are 25 uses in Narwhal's
> bytearray/bytestring tests, btw.)

Sure, but they can be replaced with assert.is(true, foo) or
equivalent, without preventing the test framework from reporting what
the expected and actual values were.

Rapha

unread,
Sep 15, 2009, 3:00:46 PM9/15/09
to CommonJS
Can I suggest a matcher-based approach, and a single assert method?

assert.that(value, matcher)

e.g.
assert.that(something, is(null))
assert.that([1,2,3], contains(2))
assert.that(['a','b','c'], matchesSomeCustomCriteriaThatIDefine)
assert.that(code, willThrow(TypeError))

I think this approach is a real win for simplicity, flexibility and
extensiblity.

Matchers could perhaps initially come from JsHamcrest.

http://github.com/danielfm/jshamcrest/tree

Raphael

On Sep 15, 8:38 pm, Tom Robinson <tlrobin...@gmail.com> wrote:
> On Sep 15, 2009, at 1:06 AM, Hannes Wallnoefer wrote:
>
>
>
> > 2009/9/15 Tom Robinson <tlrobin...@gmail.com>:

Daniel Friesen

unread,
Sep 15, 2009, 3:41:26 PM9/15/09
to comm...@googlegroups.com
I don't like that kind of method pollution.

I'd sooner go for rspec like syntax.

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Rapha

unread,
Sep 15, 2009, 5:56:43 PM9/15/09
to CommonJS
Well the matchers available need only be those explicitly imported.

e.g. something like

var is = Assert.Matchers.Core.is,
contails = Assert.Matchers.Arrays.has;

at the beginning of the appropriate scope level. Most of the time
you'd only be using a few of them anyway.

Raphael

schris...@internode.com.au

unread,
Sep 15, 2009, 9:05:47 PM9/15/09
to CommonJS
On Sep 16, 4:00 am, Rapha <rspe...@gmail.com> wrote:
> assert.that(something, is(null))
> assert.that([1,2,3], contains(2))
> assert.that(['a','b','c'], matchesSomeCustomCriteriaThatIDefine)
> assert.that(code, willThrow(TypeError))

How about a variation of this, such as:

assert(something).is(null);
assert([1,2,3]).contains(2);

This way, all of the matching methods (e.g. is(), contains(), etc) can
be contained within an Assertion object that is returned by assert()

Regards,
Scott Christopher

Kris Kowal

unread,
Sep 15, 2009, 9:15:35 PM9/15/09
to comm...@googlegroups.com
On Tue, Sep 15, 2009 at 6:05 PM, scott.chris...@gmail.com
<schris...@internode.com.au> wrote:
> How about a variation of this, such as:
>
> assert(something).is(null);
> assert([1,2,3]).contains(2);
>
> This way, all of the matching methods (e.g. is(), contains(), etc) can
> be contained within an Assertion object that is returned by assert()

Unfortunately, it would also defeat the aim of making it easily
extensible, apart from monkey patching. I'm still a proponent of
simplicity. Most tests can be broken down to assert.is(expected,
actual, message) or assert.throws(block, [Error]), and anything else
is really convenience that can be added through higher level API's.
Custom assertions need only be functions that throw
AssertionError(expected, actual, message), which seems like the
logical boundary for extension.

Kris Kowal

Ash Berlin

unread,
Sep 16, 2009, 7:08:01 AM9/16/09
to comm...@googlegroups.com

On Tue, 15 Sep 2009 18:15:35 -0700, Kris Kowal <cowber...@gmail.com>
wrote:


And i think this is the pertient point. We're never going to go get
everything we want, so the Unit test spec, which should be used in the
interop tests. should be simple:

assert.is(expected, actual);
assert.is(expected, actual, message);
assert.throws(block);
assert.throws(block, errorCtor);
assert.throws(block, message);
assert.throws(block, errorCtor, message);

I'm not sure we even need to say how we add other asserts to the core, if
we just mandate that they |throw new require('test').AssertionError( {} ) |
on error. I suggest we use a single object as a dictionary, rather than
un-named params so we can add/extend params in the future.

That way, you can do (assuming you write my-assert.js right:

var check = require('my-assert').assert;
export.testFoo = function() {
check(1).is(2, "1 is 2"); // Will throw new
require('test').AssertionError({got: 1, wanted: 2, message: "1 is 2"});
}

I think this approach gives us the best of both worlds:
1) A simple spec that we can use in the interop tests
2) Allow people to write their own test frameworks
3) But have them work together co-operatively:

var assert = require('test').assert;
var check = require('my-assert').check;
export.testFoo = function() {
assert(2,2, "2 is 2");
check(1).is(2, "1 is 2"); // throw new
require('test').AssertionError({got: 1, wanted: 2, message: "1 is 2"});
}

Please say you are all happy with this, and i will wiki it into a proposal.

Dean Landolt

unread,
Sep 16, 2009, 10:27:03 AM9/16/09
to comm...@googlegroups.com


On Wed, Sep 16, 2009 at 10:26 AM, Dean Landolt <de...@deanlandolt.com> wrote:



+1

But I would add that you should probably also standardize on the base object keys AssertionError can take. And just to bikeshed a little on the names, I really don't like "got" and "wanted". I would think "wanted" should be "expected" and got should be something like "actual" or "relieved" -- a little longer but more clear.

Firefox spell-check FAIL: s/relieved/received

Dean Landolt

unread,
Sep 16, 2009, 10:26:18 AM9/16/09
to comm...@googlegroups.com
On Wed, Sep 16, 2009 at 7:08 AM, Ash Berlin <ash_flu...@firemirror.com> wrote:

Kris Kowal

unread,
Sep 16, 2009, 5:15:47 PM9/16/09
to comm...@googlegroups.com
On Wed, Sep 16, 2009 at 4:08 AM, Ash Berlin
<ash_flu...@firemirror.com> wrote:
> assert.is(expected, actual);
> assert.is(expected, actual, message);
> assert.throws(block);
> assert.throws(block, errorCtor);
> assert.throws(block, message);
> assert.throws(block, errorCtor, message);

+1

I do think that equivalence and non-equivalence are still basic enough
that *everyone* will need them in their tests. If you mean that "is"
means "equivalence", I'd hope that it be distinguished. I think of
"is" as "identity" (as it means in Python) and "eq" as "equivalence"
(as it means in Perl, shell, and others), and has many complements
like "ne", "lt", "gt", "le", and "ge". I'm not adamantly against
verbose equivalents, but I think that these are clear and widespread.
I am ready to compromise at this point all the way down to this exact
proposal, or even different names for these extensions, but please at
least have a deep equivalence and non-equivalence assertions in the
standard.

Kris Kowal

Ash Berlin

unread,
Sep 16, 2009, 5:30:00 PM9/16/09
to comm...@googlegroups.com
Noted. I will knock this up into a wiki proposal tomorrow hopefully.

Nickolay Platonov

unread,
Sep 17, 2009, 4:57:33 AM9/17/09
to comm...@googlegroups.com
Hey guys,

Mostly for those who are interested in browser-specific area of JS:

Here is the demo of the testing tool I recently wrote: http://jsan.symbie.org/Joose3/mutability/t/

Sample test : http://joose-js.googlecode.com/svn/branches/mutability/t/055_role_to_instance_application.t.js
Project's home: http://github.com/SamuraiJack/test.run
Some docs : http://extjs-ux.org/ext-docs/?class=Test.Run

Its written quite OOPish, if someone will subclass a base 'Test.Run.Test.Browser' class with own assertions, he will receive all the UI features for free.

Test.Run can be also turned into benchmarking tool, however this will require some refactoring.

If anyone is interested please contact me.

Regards, Nickolay

Ates Goral

unread,
Sep 17, 2009, 4:30:44 PM9/17/09
to comm...@googlegroups.com
> The
> idioms for isTrue, isFalse, eqTrue, isNaN, isntNan, &c, all boil down
> to:
>
> assert.is(true, actual, message);
> assert.is(false, …);
> assert.is(false, …);
> assert.eq(true, …); // truthy
> assert.is(NaN, …);
> assert.ni(NaN, …);

If the implementation of assert.is() is done with a simple === check,
it won't work for NaN. Because NaN !== NaN.

The assert.is() implementation would then have to have special handling for NaN.

I therefore raise my hand for an assert.isNaN(actual, message).

Ates

Ates Goral

unread,
Sep 17, 2009, 4:58:51 PM9/17/09
to comm...@googlegroups.com
> If the implementation of assert.is() is done with a simple === check,
> it won't work for NaN. Because NaN !== NaN.
>
> The assert.is() implementation would then have to have special handling for NaN.
>
> I therefore raise my hand for an assert.isNaN(actual, message).

Well, after finishing reading through the entire thread and seeing the
general direction in keeping the gamut small, I guess one can always
use:

assert.is(true, isNaN(actual), msg);

Ates

Kris Kowal

unread,
Sep 17, 2009, 5:39:52 PM9/17/09
to comm...@googlegroups.com
http://wiki.commonjs.org/wiki/Unit_Testing/A

I've cut "ni" and "ne". That leaves "is" and "eq". I've also renamed
"error" to "throws"; I think that's fine.

I presume "is" is not contentious.

I see that both Ash and Hannes have pending intentions, but let's hear
the nominations for alternate names and features we can't live
without. When replying on this thread, please augment the list of
recommendations for each name so we accumulate a list.

A.) no "eq" function.
B.) "eq"
C.) "equals"
D.) "equiv"
E.) "equivalent"

Z.) no "ne" function
Y.) "ne", Kris Kowal
X.) "notEquals"
W.) "notEquiv"
V.) "notEquivalent"

1.) no "isnt" function
2.) "isnt"
3.) "ni" not identical
4.) "notIdentical"

Kris Kowal (B, Z or Y, 1 2 or 3)

Dean Landolt

unread,
Sep 17, 2009, 5:57:19 PM9/17/09
to comm...@googlegroups.com

Dean Landolt (C*, Z**, 1**)

* As long as the equality helpers themselves are spec'd and everyone agrees on those too

** Unless someone makes a compelling case for negation (I haven't considered the issues around test suite reporting)

*** I understand these caveats render any of my hand showing moot

Kris Kowal

unread,
Sep 17, 2009, 6:07:17 PM9/17/09
to comm...@googlegroups.com
On Thu, Sep 17, 2009 at 2:57 PM, Dean Landolt <de...@deanlandolt.com> wrote:

> * As long as the equality helpers themselves are spec'd and everyone agrees
> on those too

See #4 on http://wiki.commonjs.org/wiki/Unit_Testing/A

Dean Landolt

unread,
Sep 17, 2009, 6:24:57 PM9/17/09
to comm...@googlegroups.com

Oh. Didn't notice that, but great. Although as Ates pointed out, there remains the NaN != NaN gotcha. Should #4 also address this, and any other special cases?

Kris Kowal

unread,
Sep 17, 2009, 6:34:25 PM9/17/09
to comm...@googlegroups.com
On Thu, Sep 17, 2009 at 3:24 PM, Dean Landolt <de...@deanlandolt.com> wrote:
> Oh. Didn't notice that, but great. Although as Ates pointed out, there
> remains the NaN != NaN gotcha. Should #4 also address this, and any other
> special cases?

I think we should respect NaN != NaN in deep comparision. [NaN] !=
[NaN] for the same good reasons NaN != NaN or 1/0 != 2/0. It's a
harbinger of math that has lost it's meaning and is no longer useful
for such things as equivalence comparison.

Java objects throw a monkey wrench, or sabot if you're into monkey
wrenches as I know some of you are, in the machinery. I think we can
safely leave these objects outside the realm of specification.

You're right that #4 is very first draft, though. For example new
Date(0) != new Date(0), but we should treat them as equivalent.
Anybody who's willing to rewrite this section from the perspective a
vetted equivalence comparison library should.

Then there's the issue of polymorphism. Eventually I'd like to
augment this specification to defer to leftHandSide[eqMethodName] if
it exists in the prototype chain but not owned by a literal Object.
But, in the interest of getting this ratified quickly, I think we can
leave that for version 2.

Kris Kowal

Ash Berlin

unread,
Sep 18, 2009, 5:52:39 AM9/18/09
to comm...@googlegroups.com
On Thu, 17 Sep 2009 15:34:25 -0700, Kris Kowal <cowber...@gmail.com>
wrote:
>
> On Thu, Sep 17, 2009 at 3:24 PM, Dean Landolt <de...@deanlandolt.com>
> wrote:
>> Oh. Didn't notice that, but great. Although as Ates pointed out, there
>> remains the NaN != NaN gotcha. Should #4 also address this, and any
> other
>> special cases?
>
> I think we should respect NaN != NaN in deep comparision. [NaN] !=
> [NaN] for the same good reasons NaN != NaN or 1/0 != 2/0. It's a
> harbinger of math that has lost it's meaning and is no longer useful
> for such things as equivalence comparison.
>
> Java objects throw a monkey wrench, or sabot if you're into monkey
> wrenches as I know some of you are, in the machinery. I think we can
> safely leave these objects outside the realm of specification.
>
> You're right that #4 is very first draft, though. For example new
> Date(0) != new Date(0), but we should treat them as equivalent.
> Anybody who's willing to rewrite this section from the perspective a
> vetted equivalence comparison library should.

http://philrathe.com/articles/equiv handles comparing NaN to NaN and
Date(0) to Date(), both of which return true.

>
> Then there's the issue of polymorphism. Eventually I'd like to
> augment this specification to defer to leftHandSide[eqMethodName] if
> it exists in the prototype chain but not owned by a literal Object.
> But, in the interest of getting this ratified quickly, I think we can
> leave that for version 2.
>
> Kris Kowal


I've just updated the wiki page (
http://wiki.commonjs.org/wiki/Unit_Testing/A ) to change the parameters of
the AssertionError constructor from 3 arguments to a single dict/object
with named keys, and added a section on how to write custom assert
(modules).

-ash

Ash Berlin

unread,
Sep 18, 2009, 5:58:45 AM9/18/09
to comm...@googlegroups.com

On Fri, 18 Sep 2009 10:52:39 +0100, Ash Berlin
<ash_flu...@firemirror.com> wrote:

>
> http://philrathe.com/articles/equiv handles comparing NaN to NaN and
> Date(0) to Date(), both of which return true.
>

Typo - that was meant to be Date(0) to Date(0)

Ash Berlin

unread,
Sep 18, 2009, 6:15:32 AM9/18/09
to comm...@googlegroups.com

On Thu, 17 Sep 2009 14:39:52 -0700, Kris Kowal <cowber...@gmail.com>
wrote:
>
> http://wiki.commonjs.org/wiki/Unit_Testing/A

>
> A.) no "eq" function.
> B.) "eq"
> C.) "equals"
> D.) "equiv"
> E.) "equivalent"
>
> Z.) no "ne" function
> Y.) "ne", Kris Kowal
> X.) "notEquals"
> W.) "notEquiv"
> V.) "notEquivalent"
>
> 1.) no "isnt" function
> 2.) "isnt"
> 3.) "ni" not identical
> 4.) "notIdentical"

Kris Kowal (B, Z or Y, 1 2 or 3)

Ash Berlin (B, Y, 2)

But I worry that the spec |eq| doesn't cover enough cases. I would be
happier if it was either far simpler (just compares arguments using |x ==
y| no deepness etc.), or (my prefered behaviour) covers all the cases in
the equiv[0] module. (Yes I know I keep carping on about that module, but
I've been using it for a while and found it to be very good.)

[0] We're all lazy, so here's that link again:
http://philrathe.com/articles/equiv#traps Some of these shouldn't affect us
as the are IE bugs, but the list is still good.


Ates Goral

unread,
Sep 18, 2009, 2:23:46 PM9/18/09
to comm...@googlegroups.com
>> A.) no "eq" function.
>> B.) "eq"
>> C.) "equals"
>> D.) "equiv"
>> E.) "equivalent"
>>
>> Z.) no "ne" function
>> Y.) "ne", Kris Kowal
>> X.) "notEquals"
>> W.) "notEquiv"
>> V.) "notEquivalent"
>>
>> 1.) no "isnt" function
>> 2.) "isnt"
>> 3.) "ni" not identical
>> 4.) "notIdentical"
>
> Kris Kowal (B, Z or Y, 1 2 or 3)
> Ash Berlin (B, Y, 2)

Ates Goral (B, Y or neq, 2)

Hannes Wallnoefer

unread,
Sep 19, 2009, 4:06:08 AM9/19/09
to comm...@googlegroups.com
Sorry for being late with my proposal. I found I wasn't so sure about
it after all, so I turned to something else for a few days and now I
know. Here's my proposal.

equal
notEqual
same
notSame
throws
fail

Here's the same with the assert namespace and arguments included so
you get a better feel how these look in real life:

assert.equal(value1, value2, [message])
assert.notEqual(value1, value2, [message])
assert.same(value1, value2, [message])
assert.notSame(value1, value2, [message])
assert.throws(func, error, [message])
assert.fail([message])

Note that I'm using equal rather than equals because based on the
signature I assume plural form ("assert values are equal", not "assert
value 1 equals value 2".

Ideally, i would also have liked an assert() that checks one single
argument for truthyness, but I reckon assert.assert() looks awkward,
so I can live without.

2009/9/17 Kris Kowal <cowber...@gmail.com>:
>
> I presume "is" is not contentious.

As your own message earlier in this thread[1] shows, "is" can mean
both identity and equivalence. I think "same" is much more explicit to
signify identity. Plus, unlike "is", it also can be prefixed with
"not" so we don't have to get creative.

http://groups.google.com/group/commonjs/tree/browse_frm/thread/07c69484abc8a560/747d1530f13d109e?rnum=21&_done=%2Fgroup%2Fcommonjs%2Fbrowse_frm%2Fthread%2F7c69484abc8a560%3F#doc_4efa2042eca4f61c

Hannes

Neville Burnell

unread,
Sep 19, 2009, 4:27:59 AM9/19/09
to CommonJS
I'd also like to add isTrue() and isFalse() to this list.

I find it "smelly" to test equality with true and false.

On Sep 19, 6:06 pm, Hannes Wallnoefer <hann...@gmail.com> wrote:
> Sorry for being late with my proposal. I found I wasn't so sure about
> it after all, so I turned to something else for a few days and now I
> know.  Here's my proposal.
>
> equal
> notEqual
> same
> notSame
> throws
> fail
>
> Here's the same with the assert namespace and arguments included so
> you get a better feel how these look in real life:
>
> assert.equal(value1, value2, [message])
> assert.notEqual(value1, value2, [message])
> assert.same(value1, value2, [message])
> assert.notSame(value1, value2, [message])
> assert.throws(func, error, [message])
> assert.fail([message])
>
> Note that I'm using equal rather than equals because based on the
> signature I assume plural form ("assert values are equal", not "assert
> value 1 equals value 2".
>
> Ideally, i would also have liked an assert() that checks one single
> argument for truthyness, but I reckon assert.assert() looks awkward,
> so  I can live without.
>
> 2009/9/17 Kris Kowal <cowbertvon...@gmail.com>:
>
>
>
> > I presume "is" is not contentious.
>
> As your own message earlier in this thread[1] shows, "is" can mean
> both identity and equivalence. I think "same" is much more explicit to
> signify identity. Plus, unlike "is", it also can be prefixed with
> "not" so we don't have to get creative.
>
> http://groups.google.com/group/commonjs/tree/browse_frm/thread/07c694...

Tom Robinson

unread,
Sep 19, 2009, 5:56:55 AM9/19/09
to comm...@googlegroups.com
I agree we should have a basic "isTrue" type function, both
"assert.equal(foo < bar, true)" and "if (foo >= bar) assert.fail()"
are blehhh.

Can we just have the top level "assert" object be a function that
checks it's first argument is true (or truthy?)

Of course then we'd have to do var assert = require("test/
assert").assert; but that wouldn't be a dealbreaker for me, at least.

Have we decided whether equal/notEqual should be strict or not (===
and !== vs. == and !=), or have both somehow?

Also, just throwing this out there, not sure if I like it myself, but:

assert.equal
assert.not.equal
assert.same
assert.not.same

i.e. have the inverses under the "not" namespace. (doesn't work very
well for assert.not.throws, etc though)

-tom

Ash Berlin

unread,
Sep 19, 2009, 6:05:56 AM9/19/09
to comm...@googlegroups.com

On 19 Sep 2009, at 10:56, Tom Robinson wrote:

>
> I agree we should have a basic "isTrue" type function, both
> "assert.equal(foo < bar, true)" and "if (foo >= bar) assert.fail()"
> are blehhh.

And both of these give rubbish diagnostics. The aim with this thread
is to agree on a *minimal* set of asserts that we use in the
interoperability test suite, and have a defined way for other people
to add

>
> Can we just have the top level "assert" object be a function that
> checks it's first argument is true (or truthy?)

This could work.

>
> Of course then we'd have to do var assert = require("test/
> assert").assert; but that wouldn't be a dealbreaker for me, at least.
>
> Have we decided whether equal/notEqual should be strict or not (===
> and !== vs. == and !=), or have both somehow?

Judging be the presence of same/notSame, I took equals to be strict
(===) but this is one problem with the name 'equals' - its not clear
if its === or == based.

>
> Also, just throwing this out there, not sure if I like it myself, but:
>
> assert.equal
> assert.not.equal
> assert.same
> assert.not.same
>
> i.e. have the inverses under the "not" namespace. (doesn't work very
> well for assert.not.throws, etc though)

I prefer the assert.notX form.

Neville Burnell

unread,
Sep 19, 2009, 6:16:47 AM9/19/09
to CommonJS
I dont mind assert.not.X()

I guess because my background with Ruby has exposed me to some DSL
style syntax , eg rspec, where periods are used liberally

Chris Zumbrunn

unread,
Sep 19, 2009, 12:50:31 PM9/19/09
to comm...@googlegroups.com
My appologies for not yet being pleased with any of the suggested
bikeshed colors and making yet another proposal:

assert.isTrue(value, [message])
assert.isFalse(value, [message])
assert.isEqual(value1, value2, [message])
assert.notEqual(value1, value2, [message])
assert.isIdentical(value1, value2, [message])
assert.notIdentical(value1, value2, [message])


assert.throws(func, error, [message])
assert.fail([message])

Rational: More verbose is better. This way we say exactly what we
mean, without introducing any possible ambiguities and potential
confusion.

Dean Landolt

unread,
Sep 20, 2009, 2:10:29 PM9/20/09
to comm...@googlegroups.com
On Sat, Sep 19, 2009 at 5:56 AM, Tom Robinson <tlrob...@gmail.com> wrote:

I agree we should have a basic "isTrue" type function, both
"assert.equal(foo < bar, true)" and "if (foo >= bar) assert.fail()"
are blehhh.

Can we just have the top level "assert" object be a function that
checks it's first argument is true (or truthy?)

Of course then we'd have to do var assert = require("test/
assert").assert; but that wouldn't be a dealbreaker for me, at least.

But why? Looking at SecurableModules it doesn't specify this as illegal:

var assert = exports = function(condition, message){
  if(!condition)
    throw new Error("assert failed" + (message ? ": " + message : ""));
};

assert.equal = function ...
assert.same = function ...
assert.AssertionError ...

SecurableModules requires "exports" to be an object -- I'd take that to mean it could also be a function. This would render the var assert = ("test/assert").assert is unnecessary:

var assert = require("test/assert");
assert(truthyTest, "some message");
// or
assert.equal(actual, expected, "some message");

I'm already using this trick in persevere and it works nicely. I haven't checked in narwhal or any other SecurableModules implementations but I can't see why it wouldn't.

And honestly, if we can agree on a spec for AssertionError's behavior, I don't see why we need to get all fancy and agree on isTrue, notSame, etc. -- the above simple assert function is Good Enough for standard lib unit tests (with the addition of a vetted equality test lib as noted by Kris Kowal).

Can someone be more specific about exactly what we gain from a diagnostics standpoint by specing every possible comparison under the sun? I'm sure there are some benefits to be had, but can we get some specifics?

Ash Berlin

unread,
Sep 20, 2009, 2:25:31 PM9/20/09
to comm...@googlegroups.com

On 20 Sep 2009, at 19:10, Dean Landolt wrote:

>
> But why? Looking at SecurableModules it doesn't specify this as
> illegal:
>
> var assert = exports = function(condition, message){
> if(!condition)
> throw new Error("assert failed" + (message ? ": " + message :
> ""));
> };
>
> assert.equal = function ...
> assert.same = function ...
> assert.AssertionError ...
>
> SecurableModules requires "exports" to be an object -- I'd take that
> to mean it could also be a function. This would render the var
> assert = ("test/assert").assert is unnecessary:

My understanding was however that you can't replace the exports
object. The return of require is the original state of the exports
object when the module 'started'. But i can't find the wording that
made me thing about that. Point 1.3 on Modules/SecurableModules kind
of implies this is the case. I know that Flusspferd and Narwhall
behave this way (exports is the initial variable.) I'm fairly certain
that GPSEE does as well.

>
> var assert = require("test/assert");
> assert(truthyTest, "some message");
> // or
> assert.equal(actual, expected, "some message");
>
> I'm already using this trick in persevere and it works nicely. I
> haven't checked in narwhal or any other SecurableModules
> implementations but I can't see why it wouldn't.
>
> And honestly, if we can agree on a spec for AssertionError's
> behavior, I don't see why we need to get all fancy and agree on
> isTrue, notSame, etc. -- the above simple assert function is Good
> Enough for standard lib unit tests (with the addition of a vetted
> equality test lib as noted by Kris Kowal).

Yes! This is the point I've been trying to make. It might be worth not
specing *any* asserts, just the Assertion errors, and the interop
tests define a minimal set that they use.

>
> Can someone be more specific about exactly what we gain from a
> diagnostics standpoint by specing every possible comparison under
> the sun? I'm sure there are some benefits to be had, but can we get
> some specifics?


Its pretty much a case of this

asset.lt(4, 3);
// Expected: < 3
// Actual: 4

except with a nicer message or something. But to restate my point that
Dean also made. Shall we just agree on the format ofthe AssertionError
exception, and the basic format of test files (exports.testX,
test.runner(exports), etc.), and then just write some asserts for the
interop tests as neede?

Chris Zumbrunn

unread,
Sep 20, 2009, 2:57:13 PM9/20/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 20:10, Dean Landolt <de...@deanlandolt.com> wrote:
> On Sat, Sep 19, 2009 at 5:56 AM, Tom Robinson <tlrob...@gmail.com> wrote:
>>
>> I agree we should have a basic "isTrue" type function, both
>> "assert.equal(foo < bar, true)" and "if (foo >= bar) assert.fail()"
>> are blehhh.
>>
>> Can we just have the top level "assert" object be a function that
>> checks it's first argument is true (or truthy?)
>>
>> Of course then we'd have to do var assert = require("test/
>> assert").assert; but that wouldn't be a dealbreaker for me, at least.
>
> But why? Looking at SecurableModules it doesn't specify this as illegal:
>
> var assert = exports = function(condition, message){
>   if(!condition)
>     throw new Error("assert failed" + (message ? ": " + message : ""));
> };

From the spec:
2) In a module, there is a free variable called "exports", that is an
object that the module may add its API to as it executes.
3) modules must use the "exports" object as the only means of exporting.

The module must use the exports object that the implementation
provided to the module scope. That way the implementation can keep a
reference to the exports object before the module has finished
executing, mainly in order to handle potential cross dependencies
between modules. To be even clearer, maybe 3) should say: modules must
use *that* "exports" object as the only means of exporting.

> Can someone be more specific about exactly what we gain from a diagnostics
> standpoint by specing every possible comparison under the sun? I'm sure
> there are some benefits to be had, but can we get some specifics?

If the goal is solely to agree on a unit testing module for testing
the standard library itself then a minimalist approach is fine. If the
goal is to provide a unit testing module as part of the standard
library api then I think more verbose is better.

Chris

Dean Landolt

unread,
Sep 20, 2009, 3:04:13 PM9/20/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 2:25 PM, Ash Berlin <ash_flu...@firemirror.com> wrote:


On 20 Sep 2009, at 19:10, Dean Landolt wrote:

>
> But why? Looking at SecurableModules it doesn't specify this as
> illegal:
>
> var assert = exports = function(condition, message){
>   if(!condition)
>     throw new Error("assert failed" + (message ? ": " + message :
> ""));
> };
>
> assert.equal = function ...
> assert.same = function ...
> assert.AssertionError ...
>
> SecurableModules requires "exports" to be an object -- I'd take that
> to mean it could also be a function. This would render the var
> assert = ("test/assert").assert is unnecessary:

My understanding was however that you can't replace the exports
object. The return of require is the original state of the exports
object when the module 'started'. But i can't find the wording that
made me thing about that. Point 1.3 on Modules/SecurableModules kind
of implies this is the case. I know that Flusspferd and Narwhall
behave this way (exports is the initial variable.) I'm fairly certain
that GPSEE does as well.

Here's the text of 1.3:

If there is a dependency cycle, the foreign module may not have finished executing at the time it is required by one of its transitive dependencies; in this case, the object returned by "require" must contain at least the exports that the foreign module has prepared before the call to require that led to the current module's execution.

I'm having trouble reasoning about this (perhaps because it's [American] football Sunday and I've had a few). How does replacing the "exports" free variable preclude this? Is there ever a case where "exports" is injected with specific capabilities? In my mind I've always envisioned it injected as {} (but I'm very likely wrong). Exports certainly isn't immutable, so what do you mean by "exports is the initial variable"?
 

>
> var assert = require("test/assert");
> assert(truthyTest, "some message");
> // or
> assert.equal(actual, expected, "some message");
>
> I'm already using this trick in persevere and it works nicely. I
> haven't checked in narwhal or any other SecurableModules
> implementations but I can't see why it wouldn't.
>
> And honestly, if we can agree on a spec for AssertionError's
> behavior, I don't see why we need to get all fancy and agree on
> isTrue, notSame, etc. -- the above simple assert function is Good
> Enough for standard lib unit tests (with the addition of a vetted
> equality test lib as noted by Kris Kowal).

Yes! This is the point I've been trying to make.

I should have pointed out -- as anyone who's read this thread knows, you've been absolutely pleading for it (and for that equality library). I guess the point I was trying to make though is that additional unit test concepts and libraries are seemingly unnecessary to further the goals of ServerJS -- assert(truthy, message) brings all the functionality a standard lib would need (sans sane equivalence, which I agree must be spec'd), and if individual modules want more they can augment their own "assert".

Dean Landolt

unread,
Sep 20, 2009, 3:15:04 PM9/20/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 2:57 PM, Chris Zumbrunn <chris.z...@gmail.com> wrote:

On Sun, Sep 20, 2009 at 20:10, Dean Landolt <de...@deanlandolt.com> wrote:
> On Sat, Sep 19, 2009 at 5:56 AM, Tom Robinson <tlrob...@gmail.com> wrote:
>>
>> I agree we should have a basic "isTrue" type function, both
>> "assert.equal(foo < bar, true)" and "if (foo >= bar) assert.fail()"
>> are blehhh.
>>
>> Can we just have the top level "assert" object be a function that
>> checks it's first argument is true (or truthy?)
>>
>> Of course then we'd have to do var assert = require("test/
>> assert").assert; but that wouldn't be a dealbreaker for me, at least.
>
> But why? Looking at SecurableModules it doesn't specify this as illegal:
>
> var assert = exports = function(condition, message){
>   if(!condition)
>     throw new Error("assert failed" + (message ? ": " + message : ""));
> };

From the spec:
2) In a module, there is a free variable called "exports", that is an
object that the module may add its API to as it executes.
3) modules must use the "exports" object as the only means of exporting.

The module must use the exports object that the implementation
provided to the module scope. That way the implementation can keep a
reference to the exports object before the module has finished
executing, mainly in order to handle potential cross dependencies
between modules. To be even clearer, maybe 3) should say: modules must
use *that* "exports" object as the only means of exporting.

Damn. I understand now, but still -- it's such an awesome feature, and I can't think of any other way to implement it without resorting to some kind of wunderbar magic...

Ideas anyone (without changing anything about SecurableModules)? I admit this feature gains us nothing but a little clarity, but it seems so tantalizingly close it should be possible.


> Can someone be more specific about exactly what we gain from a diagnostics
> standpoint by specing every possible comparison under the sun? I'm sure
> there are some benefits to be had, but can we get some specifics?

If the goal is solely to agree on a unit testing module for testing
the standard library itself then a minimalist approach is fine. If the
goal is to provide a unit testing module as part of the standard
library api then I think more verbose is better.

I thought the goal is to spec the behavior of basic (but extensible) unit testing for not a standard library, but potentially many, such that modules in these libraries could be safely reused and shared amongst these libraries.

Ash Berlin

unread,
Sep 20, 2009, 3:30:14 PM9/20/09
to comm...@googlegroups.com
On 20 Sep 2009, at 20:15, Dean Landolt wrote:

On Sun, Sep 20, 2009 at 2:57 PM, Chris Zumbrunn <chris.z...@gmail.com> wrote:

On Sun, Sep 20, 2009 at 20:10, Dean Landolt <de...@deanlandolt.com> wrote:
> But why? Looking at SecurableModules it doesn't specify this as illegal:
>
> var assert = exports = function(condition, message){
>   if(!condition)
>     throw new Error("assert failed" + (message ? ": " + message : ""));
> };

From the spec:
2) In a module, there is a free variable called "exports", that is an
object that the module may add its API to as it executes.
3) modules must use the "exports" object as the only means of exporting.

The module must use the exports object that the implementation
provided to the module scope. That way the implementation can keep a
reference to the exports object before the module has finished
executing, mainly in order to handle potential cross dependencies
between modules. To be even clearer, maybe 3) should say: modules must
use *that* "exports" object as the only means of exporting.

Damn. I understand now, but still -- it's such an awesome feature, and I can't think of any other way to implement it without resorting to some kind of wunderbar magic...

I'm not even aware of how to do this *with* wunderbars. I know spidermonkey (and rhino) has the __noSuchMethod__ one, but that doesn't work on the object itself. I'm not even sure if its possible to turn a normal Object into something callable via an C APIs.


Ideas anyone (without changing anything about SecurableModules)? I admit this feature gains us nothing but a little clarity, but it seems so tantalizingly close it should be possible.

Yeah, I've often wanted this sort of feature. I can think of a few tricks to let you change the value of exports and still have the 'part completed circular deps' dependancy be satisified.

Dean Landolt

unread,
Sep 20, 2009, 3:54:07 PM9/20/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 3:30 PM, Ash Berlin <ash_flu...@firemirror.com> wrote:
On 20 Sep 2009, at 20:15, Dean Landolt wrote:

On Sun, Sep 20, 2009 at 2:57 PM, Chris Zumbrunn <chris.z...@gmail.com> wrote:

On Sun, Sep 20, 2009 at 20:10, Dean Landolt <de...@deanlandolt.com> wrote:
> But why? Looking at SecurableModules it doesn't specify this as illegal:
>
> var assert = exports = function(condition, message){
>   if(!condition)
>     throw new Error("assert failed" + (message ? ": " + message : ""));
> };

From the spec:
2) In a module, there is a free variable called "exports", that is an
object that the module may add its API to as it executes.
3) modules must use the "exports" object as the only means of exporting.

The module must use the exports object that the implementation
provided to the module scope. That way the implementation can keep a
reference to the exports object before the module has finished
executing, mainly in order to handle potential cross dependencies
between modules. To be even clearer, maybe 3) should say: modules must
use *that* "exports" object as the only means of exporting.

Damn. I understand now, but still -- it's such an awesome feature, and I can't think of any other way to implement it without resorting to some kind of wunderbar magic...

I'm not even aware of how to do this *with* wunderbars. I know spidermonkey (and rhino) has the __noSuchMethod__ one, but that doesn't work on the object itself. I'm not even sure if its possible to turn a normal Object into something callable via an C APIs.

I was referring to using something like wunderbars _and_ changing require (implied by my next sentence). Meaning, resorting to some kind of magic variable that gets dispatched to if exports is called.


Ideas anyone (without changing anything about SecurableModules)? I admit this feature gains us nothing but a little clarity, but it seems so tantalizingly close it should be possible.

Yeah, I've often wanted this sort of feature. I can think of a few tricks to let you change the value of exports and still have the 'part completed circular deps' dependancy be satisified.

Do tell -- I think there's real clarity to be gained from modules being callable.

Hannes Wallnoefer

unread,
Sep 20, 2009, 4:06:15 PM9/20/09
to comm...@googlegroups.com
2009/9/20 Ash Berlin <ash_flu...@firemirror.com>:
Helma NG has an include function that imports all exported properties
into the calling scope. The implementation is very straightforward
(call require and loop over properties), but only because Helma NG
uses top-level (pythonic) module scopes. We also don't have to resort
to closure magic to evaluate modules, so picking up an updated exports
object during evaluation would be literally a one line modification.
Two more reasons I'm feeling wunderbar today :)

http://www.translate.google.com/translate_t#de|en|wunderbar

Hannes

Ash Berlin

unread,
Sep 20, 2009, 4:15:42 PM9/20/09
to comm...@googlegroups.com

On 20 Sep 2009, at 20:54, Dean Landolt wrote:




Ideas anyone (without changing anything about SecurableModules)? I admit this feature gains us nothing but a little clarity, but it seems so tantalizingly close it should be possible.

Yeah, I've often wanted this sort of feature. I can think of a few tricks to let you change the value of exports and still have the 'part completed circular deps' dependancy be satisified.

Do tell -- I think there's real clarity to be gained from modules being callable.

I never said they were pretty, and I'm certainly not guaranteeing they work bug free either :)

So this method should work on a browser that has ability to define getters on an object. (Might just be FF. not sure)

function test(the_x) {
  var mod = function() {
    this.__defineGetter__('exports2', function() { return the_x });
    this.__defineSetter__('exports2', function(x) { return the_x = x  });
    print("exports2 is", exports2);
    exports2 = 456;
    print("exports2 is", exports2);
  }
  print("exports2 is", typeof exports2);
  mod();
  print("exports2 is", typeof exports2);
  print("the_x is", the_x);
}
/*
exports2 is undefined
exports2 is 1
exports2 is 456
exports2 is number
the_x is 456
*/

This method certainly won't work on a browser.

var the_exports = 123
var mod = function() {
  superSpecialFunctionThatSetsUpExport();
  
  /* rest of module text */
  print(exports);
  exports = 456;
}

and then superSpecialFunctionThatSetsUpExport() gets the scope from the scope chain, and defines a getter/setter for exports.

Kris Zyp

unread,
Sep 20, 2009, 4:17:37 PM9/20/09
to comm...@googlegroups.com
The API doesn't preclude replacing the exports variable. Replacing the
exports object is allowed in Persevere's CommonJS module loader (it
checks after the module is loaded for the value of the exports variable
in case it needs to update the memoized export object) so that you can
export a function, and I have found it to be very useful. Of course a
module that exports a function requires another module that circular
requires back, the other module could get the "wrong" exports, but in
practice this is so rare and easy to avoid that is certainly well worth
the inclusion of the functionality, at least IMO. At least replacing
exports is allowed. I had requested this be specified in the module API,
but it was rejected. Not having this certainly makes writing for any
loader (not just Persevere's) more awkward.
Kris

Ash Berlin wrote:
> On 20 Sep 2009, at 20:15, Dean Landolt wrote:
>
>> On Sun, Sep 20, 2009 at 2:57 PM, Chris Zumbrunn
>> <chris.z...@gmail.com <mailto:chris.z...@gmail.com>> wrote:
>>
>>
>> On Sun, Sep 20, 2009 at 20:10, Dean Landolt <de...@deanlandolt.com
>> <mailto:de...@deanlandolt.com>> wrote:
>> > But why? Looking at SecurableModules it doesn't specify this as
>> illegal:
>> >
>> > var assert = exports = function(condition, message){
>> > if(!condition)
>> > throw new Error("assert failed" + (message ? ": " + message
>> : ""));
>> > };
>>
>> From the spec:
>> 2) In a module, there is a free variable called "exports", that is an
>> object that the module may add its API to as it executes.
>> 3) modules must use the "exports" object as the only means of
>> exporting.
>>
>> The module must use the exports object that the implementation
>> provided to the module scope. That way the implementation can keep a
>> reference to the exports object before the module has finished
>> executing, mainly in order to handle potential cross dependencies
>> between modules. To be even clearer, maybe 3) should say: modules
>> must
>> use *that* "exports" object as the only means of exporting.
>>
>>
>> Damn. I understand now, but still -- it's such an /awesome/ feature,
>> and I can't think of any other way to implement it without resorting
>> to some kind of wunderbar magic...
>
> I'm not even aware of how to do this *with* wunderbars. I know
> spidermonkey (and rhino) has the __noSuchMethod__ one, but that
> doesn't work on the object itself. I'm not even sure if its possible
> to turn a normal Object into something callable via an C APIs.
>
>>
>> Ideas anyone (without changing /anything/ about SecurableModules)? I

Hannes Wallnoefer

unread,
Sep 20, 2009, 4:27:50 PM9/20/09
to comm...@googlegroups.com
2009/9/20 Hannes Wallnoefer <han...@gmail.com>:
As an example, this is what a unit test (in this case, a converted
Narwhal ByteArray test) looks with helma/unittest:

http://github.com/hns/helma-ng/blob/master/modules/test/binary/bytearray-tests.js

As you see, you _can_ have simple module importing without the need to
compromise function names for namespaced module.function() usage.

Now since this thread is about a CommonJS unit test spec and
require/exports is the only thing standardized in CommonJS, I also
optimized my proposal for that kind of usage (assert.equal,
assert.same, etc).

But I can't help noticing how raw securable module loading leads to a
very verbose programming style, with lots of exports.foo = exports.bar
all over the place, and people trying to trick some elegance out of
the system.

Hannes

Tom Robinson

unread,
Sep 20, 2009, 4:47:10 PM9/20/09
to comm...@googlegroups.com

On Sep 20, 2009, at 1:17 PM, Kris Zyp wrote:

>
> The API doesn't preclude replacing the exports variable. Replacing the
> exports object is allowed in Persevere's CommonJS module loader (it
> checks after the module is loaded for the value of the exports
> variable
> in case it needs to update the memoized export object) so that you can
> export a function, and I have found it to be very useful. Of course a
> module that exports a function requires another module that circular
> requires back, the other module could get the "wrong" exports, but in
> practice this is so rare and easy to avoid that is certainly well
> worth
> the inclusion of the functionality, at least IMO. At least replacing
> exports is allowed. I had requested this be specified in the module
> API,
> but it was rejected. Not having this certainly makes writing for any
> loader (not just Persevere's) more awkward.
> Kris

It's not precluded, but it's not defined either. It won't work in
Narwhal, and I suspect most other implementations (though I don't
think it would be hard to add it, at least to Narwhal)

I think it should be left undefined, due to the circular require
problem.
You *could* set up the modules system to use a function that forwards
to some special exported property (call it "__callable__" in this case):

var moduleObject = function() {
if (typeof moduleObject.__callable__ === "function")
return moduleObject.__callable__.apply(this, arguments);
else
throw "Not callable";
}

instead of "moduleObject = {}" or whatever we currently use.

Then in the "assert" module you would just do

exports.__callable__ = function() {
// assert code
}

etc.

This should work everywhere (doesn't rely on __noSuchMethod__) and
won't suffer from the circular dependency problem that replacing
"exports" causes.

Also, there may be cases where moduleObject needs to be some special
kind of object, for example in native modules that require a
destructor. Perhaps the projects that are further along with native
module can comment on that.

Dean Landolt

unread,
Sep 20, 2009, 5:21:30 PM9/20/09
to comm...@googlegroups.com

This is what I was suggesting as the wunderbar solution, but as far as I can tell, in order for it to work across implementations the __callable__ (or whatever else) key would have to be spec'd in SecurableModules -- and changing SecurableModules, the only ratified spec, is probably out of the question (not to mention the fact that introducing this kind of impure magic would probably upset some folks).

That leaves me to wonder, is there another way? Does replacing "exports" definitely violate the spec?

As I've inadvertently hijacked this thread (uh, it was about unit testing, right?), so this is the last I'll say on the subject -- it looks like Ash started another thread so I'll take up this conversation there.

So back on topic, what is the absolutely BARE minimum a ServerJS-conformant "assert" implementation should provide? Ash says sane equivalence testing and a standardized AssertionError. What else is really needed?

Tom Robinson

unread,
Sep 20, 2009, 5:41:34 PM9/20/09
to comm...@googlegroups.com

On Sep 20, 2009, at 2:21 PM, Dean Landolt wrote:

> This is what I was suggesting as the wunderbar solution, but as far
> as I can tell, in order for it to work across implementations the
> __callable__ (or whatever else) key would have to be spec'd in
> SecurableModules -- and changing SecurableModules, the only ratified
> spec, is probably out of the question (not to mention the fact that
> introducing this kind of impure magic would probably upset some
> folks).

Yes it would need to be added to the spec. There's no other way right
now.

> That leaves me to wonder, is there another way? Does replacing
> "exports" definitely violate the spec?

It doesn't *violate* the spec, but it's not defined, so
implementations are *not* required to support it, thus you cannot rely
on it in CommonJS compatible modules.

>
> As I've inadvertently hijacked this thread (uh, it was about unit
> testing, right?), so this is the last I'll say on the subject -- it
> looks like Ash started another thread so I'll take up this
> conversation there.
>
> So back on topic, what is the absolutely BARE minimum a ServerJS-
> conformant "assert" implementation should provide? Ash says sane
> equivalence testing and a standardized AssertionError. What else is
> really needed?

In my opinion:

truthy ("assert", "isTrue")
identity (===, "is", "same")
equivalence ("eq", "equiv")
throws exception ("throws")

and the inverses of these four.

Ash Berlin

unread,
Sep 20, 2009, 5:54:03 PM9/20/09
to comm...@googlegroups.com
I can agree to this list. (Previously my implementation of same was fuzzy, but looking up the dict defn: "1: identical; not different; unchanged")

Now its just a question of naming. Green! No Blue!

-ash

Kris Kowal

unread,
Sep 20, 2009, 9:15:10 PM9/20/09
to comm...@googlegroups.com

Kris Kowal

unread,
Sep 20, 2009, 9:19:13 PM9/20/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 2:41 PM, Tom Robinson <tlrob...@gmail.com> wrote:
> truthy ("assert", "isTrue")
> identity (===, "is", "same")
> equivalence ("eq", "equiv")
> throws exception ("throws")
>
> and the inverses of these four.

+1

Daniel Friesen

unread,
Sep 20, 2009, 7:35:02 PM9/20/09
to comm...@googlegroups.com
Yes, the exports object can't be replaced. I ran into the unfortunate
reason why while implementing require in MonkeyScript Lite.
If you require something and that module retuires or even leads to a
chain that ends up requiring the module you are already in the middle
of requiring the script won't have finished and you need to either
return a partial (Impossible if you allow it to be replaced) or throw
a failure error and tell the programmer not to loop like that.

Unfortunately that kind of require is fundamental to SecurableModules.
Because if you write a File and Path class in two separate modules and
they each use a method that uses the other class (such as
complementary toFile and toPath conversions) the only way to get those
classes is to require them.

~Daniel Friesen (DanTMan, Nadir Seen Fire) [http://daniel.friesen.name]

On 20-Sep-09, at 11:25 AM, Ash Berlin <ash_flu...@firemirror.com>
wrote:

>
>
> On 20 Sep 2009, at 19:10, Dean Landolt wrote:
>
>>
>> But why? Looking at SecurableModules it doesn't specify this as
>> illegal:
>>
>> var assert = exports = function(condition, message){
>> if(!condition)
>> throw new Error("assert failed" + (message ? ": " + message :
>> ""));
>> };
>>

Dean Landolt

unread,
Sep 20, 2009, 10:03:57 PM9/20/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 9:15 PM, Kris Kowal <cowber...@gmail.com> wrote:

For those interested, we've had this discussion before:

http://groups.google.com/group/commonjs/browse_thread/thread/6a6a4fe4ce4c0200/0673da702ddfc50d?#0673da702ddfc50d

Thanks for the pointer to this thread. I just read it over though and to me the discussion seems to hinge on some on specifying some magic "init" key (be it init, initialize as Kris Zyp called for, or _callable_ as Tom recently suggested). I agree that going in this direction may be a little impure, and at the very least would require a change to SecurableModules.

What I was wondering was why we can't simply replace the exports object with a function? That function could be a constructor or just a simple functional assert implementation, and can then have additional exported functions tacked onto it. It's a very simple approach to solving a problem almost everyone seems to want solved, but it's definitely a grey area in the spec.

There are certainly enough folks here that have implemented SecurableModules to be able to answer to whether it would be possible to allow replacing the exports object. This should be explicitly specified either way, but module authors would gain some nice flexibility if it were specified as allowed.

Dean Landolt

unread,
Sep 20, 2009, 10:10:36 PM9/20/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 7:35 PM, Daniel Friesen <nadir.s...@gmail.com> wrote:

Yes, the exports object can't be replaced. I ran into the unfortunate
reason why while implementing require in MonkeyScript Lite.
If you require something and that module retuires or even leads to a
chain that ends up requiring the module you are already in the middle
of requiring the script won't have finished and you need to either
return a partial (Impossible if you allow it to be replaced) or throw
a failure error and tell the programmer not to loop like that.

Unfortunately that kind of require is fundamental to SecurableModules.
Because if you write a File and Path class in two separate modules and
they each use a method that uses the other class (such as
complementary toFile and toPath conversions) the only way to get those
classes is to require them.

Understood, but why can't you have both? I'd certainly be willing to live with the limitation of no circular dependencies in modules where I replace the exports object -- we'd still be gaining a useful, if slightly limited feature, without sacrificing any of the functionality we have now.

Kris Kowal

unread,
Sep 21, 2009, 1:16:51 AM9/21/09
to comm...@googlegroups.com
On Sun, Sep 20, 2009 at 7:10 PM, Dean Landolt <de...@deanlandolt.com> wrote:
> Understood, but why can't you have both? I'd certainly be willing to live
> with the limitation of no circular dependencies in modules where I replace
> the exports object -- we'd still be gaining a useful, if slightly limited
> feature, without sacrificing any of the functionality we have now.

In pure-js module loaders, this would involve injecting an expression
that returns the new exports value and reassigning it to the module
instance memo.

// "text" is the content of the module file.
// "id" is the top-level identifier of the module

evaluate = function (text, id) {
return new Function(
"require",
"exports",
"module",
text + "\n//*/\nreturn exports" // hack
);
};

modules[id] = exports;
var factory = loader.load(id);
modules[id] = factory(Require(id), exports, Module(id, path));

That is to say that this feature would be a hack, none worse than any
we've committed in the past.

However, my intention is to support the idea of standardizing a secure
module loader for ES-Harmony in terms of a "hermetic evaluator", an
"eval" that the security folks can trust to construct modules from
suspicious code. The hermetic evaluator would take a program and a
list of names to inject in its lexical scope, and return a function
that executes the program with particular respective values in a
deeply frozen global scope. That, and variations of that idea, would
be an elegant design with many uses. Twisting the hermetic evaluator
so that it returns the last known values of the injected
variables…would be hard to fathom and I don't think it would fly.

Kris Kowal

Aristid Breitkreuz

unread,
Sep 21, 2009, 6:17:06 AM9/21/09
to comm...@googlegroups.com
Dean Landolt schrieb:

> Understood, but why can't you have both? I'd certainly be willing to
> live with the limitation of no circular dependencies in modules where
> I replace the exports object -- we'd still be gaining a useful, if
> slightly limited feature, without sacrificing any of the functionality
> we have now.

If you implement CommonJS, you can support that feature. Just don't
expect other implementations to support it.

Wes Garland

unread,
Sep 21, 2009, 10:42:00 AM9/21/09
to comm...@googlegroups.com
Dean:

I'm not addressing the question of whether we should have callable exports or not in this post, however, I will address your implementation query.

Making the exports object replaceable definitely has implementation impacts. Both flusspferd and gpsee implement blended modules where a native exports object is monkey-patched by JavaScript code.

GPSEE also uses instances of a specific class for exports objects; a native class with no JS constructor. This special class can be used to help find the object on the scope chain for certain internal operations, and also helps us to garbage collect DSO modules.

I don't see either of these as show stoppers, but module exports were spec'd as irreplaceable from a security POV. I forget the exact details -- they're archived somewhere -- but I rather like 'em like that.  Restricting a particular module to a single class also has long-term "cruft management" issues where you're likely to have excessive inter-module dependencies, properties-on-functions and other "ugly" things.

GPSEE had the equivalent of callable exports long before Securable Modules even existed, and while I thought I liked that feature, I found that I did not miss it all when it went away.

Wes

--
Wesley W. Garland
Director, Product Development
PageMail, Inc.
+1 613 542 2787 x 102

Dean Landolt

unread,
Sep 21, 2009, 10:48:30 AM9/21/09
to comm...@googlegroups.com

Thanks Tom. Between your and Kris Kowal's answers, it's very clear that replacing tje exports object is not just problematic but simply undesirable. It just struck me as the easiest way to avoid having to pick the paint colors of an interoperable assert design (and presumably others in the future), but oh well.

George Moschovitis

unread,
Sep 22, 2009, 6:31:56 AM9/22/09
to CommonJS
> I would prefer to keep it slightly more verbose.

+1
Reply all
Reply to author
Forward
0 new messages