Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moved test-class-constructor into suite as Class/ConstructorTest
Also removed assertions that are performed elsewhere. We broke onto the fifth line in the suite ;)
- Loading branch information
1 parent
d0bc34a
commit ee886f2
Showing
2 changed files
with
203 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
/** | ||
* Tests class module constructor creation | ||
* | ||
* Copyright (C) 2010, 2011, 2012, 2013 Mike Gerwitz | ||
* | ||
* This file is part of GNU ease.js. | ||
* | ||
* ease.js is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
require( 'common' ).testCase( | ||
{ | ||
setUp: function() | ||
{ | ||
this.Sut = this.require( 'class' ); | ||
}, | ||
|
||
|
||
/** | ||
* As a sanity check, ensure that the constructor is not invoked upon | ||
* defining the class. (Note that the case of ensuring that it is not | ||
* called when creating a subtype is handled by the ExtendTest case.) | ||
*/ | ||
'Constructor should not be invoked before instantiation': function() | ||
{ | ||
var called = false; | ||
this.Sut.extend( { __construct: function() { called = true; } } ); | ||
|
||
this.assertNotEqual( called, true ); | ||
}, | ||
|
||
|
||
/** | ||
* Since __construct is a special method that is not recognized by | ||
* ECMAScript itself, we must ensure that it is invoked when the class | ||
* is instantiated. Further, it should only be called a single time, | ||
* which is particularly important if it produces side-effects. | ||
*/ | ||
'Constructor should be invoked once upon instantiation': function() | ||
{ | ||
var called = 0; | ||
|
||
var Foo = this.Sut.extend( | ||
{ | ||
__construct: function() { called++; } | ||
} ); | ||
|
||
// note that we're not yet testing the more consise new-less | ||
// invocation style | ||
new Foo(); | ||
this.assertEqual( called, 1 ); | ||
}, | ||
|
||
|
||
/** | ||
* Once invoked, the __construct method should be bound to the newly | ||
* created instance. | ||
*/ | ||
'Constructor should be invoked within context of new instance': | ||
function() | ||
{ | ||
var expected = Math.random(); | ||
|
||
var Foo = this.Sut.extend( | ||
{ | ||
val: null, | ||
__construct: function() { this.val = expected; } | ||
} ); | ||
|
||
// if `this' was bound to the instance, then __construct should set | ||
// VAL to EXPECTED | ||
var inst = new Foo(); | ||
this.assertEqual( inst.val, expected ); | ||
}, | ||
|
||
|
||
/** | ||
* All arguments passed to the constructor (that is, by invoking the | ||
* ``class'') should be passed to __construct, unchanged and | ||
* uncopied---that is, references should be retained. | ||
*/ | ||
'Constructor arguments should be passed unchanged to __construct': | ||
function() | ||
{ | ||
var args = [ "foo", { bar: 'baz' }, [ 'moo', 'cow' ] ], | ||
given = null; | ||
|
||
var Foo = this.Sut.extend( | ||
{ | ||
__construct: function() | ||
{ | ||
given = Array.prototype.slice.call( arguments, 0 ); | ||
} | ||
} ); | ||
|
||
new Foo( args[ 0 ], args[ 1 ], args[ 2 ] ); | ||
|
||
// make sure we have everything and didn't get anything extra | ||
this.assertEqual( given.length, args.length ); | ||
|
||
var i = args.length; | ||
while ( i-- ) | ||
{ | ||
this.assertStrictEqual( given[ i ], args[ i ], | ||
"Ctor argument mismatch: " + i | ||
); | ||
} | ||
}, | ||
|
||
|
||
/** | ||
* If a subtype does not define its own constructor, then its parent's | ||
* should be called by default. Note that this behavior---as is clear by | ||
* the name __construct---is modelled after PHP; Java classes, for | ||
* instance, do not inherit their parents' constructors. | ||
*/ | ||
'Parent constructor should be invoked for subtype if not overridden': | ||
function() | ||
{ | ||
var called = false; | ||
|
||
var Sub = this.Sut.extend( | ||
{ | ||
__construct: function() { called = true; } | ||
} ).extend( {} ); | ||
|
||
new Sub(); | ||
this.assertOk( called ); | ||
}, | ||
|
||
|
||
/** | ||
* Classes created through ease.js do not require use of the `new' | ||
* keyword, which allows for a much more natural, concise, and less | ||
* error-prone syntax. Ensure that a new instance is created even when | ||
* it is omitted. | ||
* | ||
* The rest of the tests above would then stand, since they use the | ||
* `new' keyword and this concise format has no choice but to ultimately | ||
* do the same; otherwise, it would not be recognized by instanceof. | ||
*/ | ||
'Constructor does not require `new\' keyword': function() | ||
{ | ||
var Foo = this.Sut.extend( {} ); | ||
|
||
this.assertOk( new Foo() instanceof Foo ); // sanity check | ||
this.assertOk( Foo() instanceof Foo ); | ||
}, | ||
|
||
|
||
|
||
/** | ||
* In certain OO languages, one would prevent a class from being | ||
* instantiated by declaring the constructor as protected or private. To | ||
* me (Mike Gerwitz), this is cryptic. A better method would simply be | ||
* to throw an exception. Perhaps, in the future, an alternative will be | ||
* provided for consistency. | ||
* | ||
* The constructor must be public. (It is for this reason that you will | ||
* often see the convention of omitting visibility keywords entirely for | ||
* __construct, since public is the default and there is no other | ||
* option.) | ||
*/ | ||
'__construct must be public': function() | ||
{ | ||
var Sut = this.Sut; | ||
|
||
this.assertThrows( function() | ||
{ | ||
Sut( { 'protected __construct': function() {} } ); | ||
}, TypeError, "Constructor should not be able to be protected" ); | ||
|
||
this.assertThrows( function() | ||
{ | ||
Sut( { 'private __construct': function() {} } ); | ||
}, TypeError, "Constructor should not be able to be private" ); | ||
}, | ||
|
||
|
||
/** | ||
* When a constructor is instantiated conventionally in ECMAScript, the | ||
* instance's `constructor' property is set to the constructor that was | ||
* used to instantiate it. The same should be true for class instances. | ||
* | ||
* This will also be important for reflection. | ||
*/ | ||
'`constructor\' property is properly set to class object': function() | ||
{ | ||
var Foo = this.Sut.extend( {} ); | ||
this.assertStrictEqual( Foo().constructor, Foo ); | ||
}, | ||
} ); |
This file was deleted.
Oops, something went wrong.