前回に引き続いて、
Test.Unit.Assertions
Test.
0251:Test.Unit.Assertions = Class.create();
0252:Test.Unit.Assertions.prototype = {
0253: initialize: function() {
0254: this.assertions = 0;
0255: this.failures = 0;
0256: this.errors = 0;
0257: this.messages = [];
0258: },
252~258行目のinitializeは、
0259: summary: function() {
0260: return (
0261: this.assertions + " assertions, " +
0262: this.failures + " failures, " +
0263: this.errors + " errors" + "\n" +
0264: this.messages.join("\n"));
0265: },
259~265行目のsummaryは、
0266: pass: function() {
0267: this.assertions++;
0268: },
266~268行目のpassは、
0269: fail: function(message) {
0270: this.failures++;
0271: this.messages.push("Failure: " + message);
0272: },
269~272行目のfailは、
0273: info: function(message) {
0274: this.messages.push("Info: " + message);
0275: },
273~275行目のinfoは、
0276: error: function(error) {
0277: this.errors++;
0278: this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
0279: },
276~279行目のerrorは、
0280: status: function() {
0281: if (this.failures > 0) return 'failed';
0282: if (this.errors > 0) return 'error';
0283: return 'passed';
0284: },
280~284行目のstatusは、
アサートのための関数群
0285: assert: function(expression) {
0286: var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
0287: try { expression ? this.pass() :
0288: this.fail(message); }
0289: catch(e) { this.error(e); }
0290: },
285~290行目のassertは、
0291: assertEqual: function(expected, actual) {
0292: var message = arguments[2] || "assertEqual";
0293: try { (expected == actual) ? this.pass() :
0294: this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
0295: '", actual "' + Test.Unit.inspect(actual) + '"'); }
0296: catch(e) { this.error(e); }
0297: },
291~297行目のassertEqualは、
0298: assertInspect: function(expected, actual) {
0299: var message = arguments[2] || "assertInspect";
0300: try { (expected == actual.inspect()) ? this.pass() :
0301: this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
0302: '", actual "' + Test.Unit.inspect(actual) + '"'); }
0303: catch(e) { this.error(e); }
0304: },
298~304行目のassertInspectは、
0305: assertEnumEqual: function(expected, actual) {
0306: var message = arguments[2] || "assertEnumEqual";
0307: try { $A(expected).length == $A(actual).length &&
0308: expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
0309: this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
0310: ', actual ' + Test.Unit.inspect(actual)); }
0311: catch(e) { this.error(e); }
0312: },
305~312行目のassertEnumEqualは、
0313: assertNotEqual: function(expected, actual) {
0314: var message = arguments[2] || "assertNotEqual";
0315: try { (expected != actual) ? this.pass() :
0316: this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
0317: catch(e) { this.error(e); }
0318: },
313~318行目のassertNotEqualは、
0319: assertIdentical: function(expected, actual) {
0320: var message = arguments[2] || "assertIdentical";
0321: try { (expected === actual) ? this.pass() :
0322: this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
0323: '", actual "' + Test.Unit.inspect(actual) + '"'); }
0324: catch(e) { this.error(e); }
0325: },
319~325行目のassertIdenticalは、
0326: assertNotIdentical: function(expected, actual) {
0327: var message = arguments[2] || "assertNotIdentical";
0328: try { !(expected === actual) ? this.pass() :
0329: this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
0330: '", actual "' + Test.Unit.inspect(actual) + '"'); }
0331: catch(e) { this.error(e); }
0332: },
326~332行目のassertNotIdenticalは、
0333: assertNull: function(obj) {
0334: var message = arguments[1] || 'assertNull'
0335: try { (obj==null) ? this.pass() :
0336: this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
0337: catch(e) { this.error(e); }
0338: },
333~338行目のassertNullは、
0339: assertMatch: function(expected, actual) {
0340: var message = arguments[2] || 'assertMatch';
0341: var regex = new RegExp(expected);
0342: try { (regex.exec(actual)) ? this.pass() :
0343: this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
0344: catch(e) { this.error(e); }
0345: },
339~345行目のassertMatchは、
0346: assertHidden: function(element) {
0347: var message = arguments[1] || 'assertHidden';
0348: this.assertEqual("none", element.style.display, message);
0349: },
346~349行目のassertHiddenは、
0350: assertNotNull: function(object) {
0351: var message = arguments[1] || 'assertNotNull';
0352: this.assert(object != null, message);
0353: },
350~353行目のassertNotNullは、
0354: assertType: function(expected, actual) {
0355: var message = arguments[2] || 'assertType';
0356: try {
0357: (actual.constructor == expected) ? this.pass() :
0358: this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
0359: '", actual "' + (actual.constructor) + '"'); }
0360: catch(e) { this.error(e); }
0361: },
354~361行目のassertTypeは、
0362: assertNotOfType: function(expected, actual) {
0363: var message = arguments[2] || 'assertNotOfType';
0364: try {
0365: (actual.constructor != expected) ? this.pass() :
0366: this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
0367: '", actual "' + (actual.constructor) + '"'); }
0368: catch(e) { this.error(e); }
0369: },
362~369行目のassertNotOfTypeは、
0370: assertInstanceOf: function(expected, actual) {
0371: var message = arguments[2] || 'assertInstanceOf';
0372: try {
0373: (actual instanceof expected) ? this.pass() :
0374: this.fail(message + ": object was not an instance of the expected type"); }
0375: catch(e) { this.error(e); }
0376: },
370~376行目のassertInstanceOfは、
0377: assertNotInstanceOf: function(expected, actual) {
0378: var message = arguments[2] || 'assertNotInstanceOf';
0379: try {
0380: !(actual instanceof expected) ? this.pass() :
0381: this.fail(message + ": object was an instance of the not expected type"); }
0382: catch(e) { this.error(e); }
0383: },
377~383行目のassertNotInstanceOfは、
0384: assertRespondsTo: function(method, obj) {
0385: var message = arguments[2] || 'assertRespondsTo';
0386: try {
0387: (obj[method] && typeof obj[method] == 'function') ? this.pass() :
0388: this.fail(message + ": object doesn't respond to [" + method + "]"); }
0389: catch(e) { this.error(e); }
0390: },
384~390行目のassertRespondsToは、
0391: assertReturnsTrue: function(method, obj) {
0392: var message = arguments[2] || 'assertReturnsTrue';
0393: try {
0394: var m = obj[method];
0395: if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
0396: m() ? this.pass() :
0397: this.fail(message + ": method returned false"); }
0398: catch(e) { this.error(e); }
0399: },
391~399行目のassertReturnsTrueは、
0400: assertReturnsFalse: function(method, obj) {
0401: var message = arguments[2] || 'assertReturnsFalse';
0402: try {
0403: var m = obj[method];
0404: if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
0405: !m() ? this.pass() :
0406: this.fail(message + ": method returned true"); }
0407: catch(e) { this.error(e); }
0408: },
400~408行目のassertReturnsFalseは、
0409: assertRaise: function(exceptionName, method) {
0410: var message = arguments[2] || 'assertRaise';
0411: try {
0412: method();
0413: this.fail(message + ": exception expected but none was raised"); }
0414: catch(e) {
0415: ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e);
0416: }
0417: },
409~417行目のassertRaiseは、
0418: assertElementsMatch: function() {
0419: var expressions = $A(arguments), elements = $A(expressions.shift());
0420: if (elements.length != expressions.length) {
0421: this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
0422: return false;
0423: }
0424: elements.zip(expressions).all(function(pair, index) {
0425: var element = $(pair.first()), expression = pair.last();
0426: if (element.match(expression)) return true;
0427: this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
0428: }.bind(this)) && this.pass();
0429: },
418~429行目のassertElementsMatchは、
0430: assertElementMatches: function(element, expression) {
0431: this.assertElementsMatch([element], expression);
0432: },
430~432行目のassertElementMatchesは、
0433: benchmark: function(operation, iterations) {
0434: var startAt = new Date();
0435: (iterations || 1).times(operation);
0436: var timeTaken = ((new Date())-startAt);
0437: this.info((arguments[2] || 'Operation') + ' finished ' +
0438: iterations + ' iterations in ' + (timeTaken/1000)+'s' );
0439: return timeTaken;
0440: },
433~440行目のbenchmarkは、
437行目で、
0441: _isVisible: function(element) {
0442: element = $(element);
0443: if(!element.parentNode) return true;
0444: this.assertNotNull(element);
0445: if(element.style && Element.getStyle(element, 'display') == 'none')
0446: return false;
0447:
0448: return this._isVisible(element.parentNode);
0449: },
441~449行目の_isVisibleは、
448行目で、
0450: assertNotVisible: function(element) {
0451: this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
0452: },
450~452行目のassertNotVisibleは、
0453: assertVisible: function(element) {
0454: this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
0455: },
453~455行目のassertVisibleは、
0456: benchmark: function(operation, iterations) {
0457: var startAt = new Date();
0458: (iterations || 1).times(operation);
0459: var timeTaken = ((new Date())-startAt);
0460: this.info((arguments[2] || 'Operation') + ' finished ' +
0461: iterations + ' iterations in ' + (timeTaken/1000)+'s' );
0462: return timeTaken;
0463: }
0464:}
0465:
benchmarkが2回、
Test.Unit.Testcase
Test.
0466:Test.Unit.Testcase = Class.create();
0467:Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
0468: initialize: function(name, test, setup, teardown) {
0469: Test.Unit.Assertions.prototype.initialize.bind(this)();
0470: this.name = name;
0471:
0472: if(typeof test == 'string') {
0473: test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
0474: test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
0475: this.test = function() {
0476: eval('with(this){'+test+'}');
0477: }
0478: } else {
0479: this.test = test || function() {};
0480: }
0481:
0482: this.setup = setup || function() {};
0483: this.teardown = teardown || function() {};
0484: this.isWaiting = false;
0485: this.timeToWait = 1000;
0486: },
468~486行目のinitializeは、
469行目で、
470行目の、
472~477行目で、
473、
475行目で、
482行目のsetupは、
483行目のteardownは、
484行目のisWaitingは、
485行目のtimeToWaitは、
0487: wait: function(time, nextPart) {
0488: this.isWaiting = true;
0489: this.test = nextPart;
0490: this.timeToWait = time;
0491: },
487~491行目のwaitは、
489行目で、
0492: run: function() {
0493: try {
0494: try {
0495: if (!this.isWaiting) this.setup.bind(this)();
0496: this.isWaiting = false;
0497: this.test.bind(this)();
0498: } finally {
0499: if(!this.isWaiting) {
0500: this.teardown.bind(this)();
0501: }
0502: }
0503: }
0504: catch(e) { this.error(e); }
0505: }
0506:});
0507:
492~506行目のrunは、
495行目で、
497行目で、
498行目で、
504行目で、
BDDスタイルのテスト記述
BDDはビヘイビア駆動開発の略で、
0001:Test.context("BDD-style testing",{
0002:
0003: setup: function() {
0004: },
0005:
0006: teardown: function() {
0007: },
0008:
0009: 'should automatically add extensions to strings': function(){
0010: 'a'.shouldEqual('a');
0011: 'a'.shouldNotEqual('b');
0012: 'a'.shouldNotBeNull();
0013: 'a'.shouldBeA(String);
0014:
0015: var aString = 'boo!';
0016: aString.shouldEqual('boo!');
0017: aString.shouldBeA(String);
0018: aString.shouldNotBeA(Number);
0019: },
0020:})
それではコードに戻りましょう。
0508:// *EXPERIMENTAL* BDD-style testing to please non-technical folk
0509:// This draws many ideas from RSpec http://rspec.rubyforge.org/
0510:
0511:Test.setupBDDExtensionMethods = function(){
0512: var METHODMAP = {
0513: shouldEqual: 'assertEqual',
0514: shouldNotEqual: 'assertNotEqual',
0515: shouldEqualEnum: 'assertEnumEqual',
0516: shouldBeA: 'assertType',
0517: shouldNotBeA: 'assertNotOfType',
0518: shouldBeAn: 'assertType',
0519: shouldNotBeAn: 'assertNotOfType',
0520: shouldBeNull: 'assertNull',
0521: shouldNotBeNull: 'assertNotNull',
0522:
0523: shouldBe: 'assertReturnsTrue',
0524: shouldNotBe: 'assertReturnsFalse',
0525: shouldRespondTo: 'assertRespondsTo'
0526: };
0527: var makeAssertion = function(assertion, args, object) {
0528: this[assertion].apply(this,(args || []).concat([object]));
0529: }
0530:
0531: Test.BDDMethods = {};
0532: $H(METHODMAP).each(function(pair) {
0533: Test.BDDMethods[pair.key] = function() {
0534: var args = $A(arguments);
0535: var scope = args.shift();
0536: makeAssertion.apply(scope, [pair.value, args, this]); };
0537: });
0538:
0539: [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
0540: function(p){ Object.extend(p, Test.BDDMethods) }
0541: );
0542:}
0543:
508~543行目のTest.
512~526行目は、
527行目のmakeAssertionは、
531行目で、
532~537行目で、
539~541行目で、
0544:Test.context = function(name, spec, log){
0545: Test.setupBDDExtensionMethods();
0546:
0547: var compiledSpec = {};
0548: var titles = {};
0549: for(specName in spec) {
0550: switch(specName){
0551: case "setup":
0552: case "teardown":
0553: compiledSpec[specName] = spec[specName];
0554: break;
0555: default:
0556: var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
0557: var body = spec[specName].toString().split('\n').slice(1);
0558: if(/^\{/.test(body[0])) body = body.slice(1);
0559: body.pop();
0560: body = body.map(function(statement){
0561: return statement.strip()
0562: });
0563: compiledSpec[testName] = body.join('\n');
0564: titles[testName] = specName;
0565: }
0566: }
0567: new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
0568:};
544~568行目のTest.
545行目で、
551、
555行目以降が本質的な部分です。
556行目で、
557行目の、
はじめの状態です。
function()
{
'a'.shouldEqual('a');
'a'.shouldNotEqual('b');
'a'.shouldNotBeNull();
'a'.shouldBeA(String);
}
557行目のslice(1)で、
{
'a'.shouldEqual('a');
'a'.shouldNotEqual('b');
'a'.shouldNotBeNull();
'a'.shouldBeA(String);
}
558行目のslice(1)で、
この処理は'function(){'の書法ならば必要ないので、
'a'.shouldEqual('a');
'a'.shouldNotEqual('b');
'a'.shouldNotBeNull();
'a'.shouldBeA(String);
}
559行目のpop()で、
'a'.shouldEqual('a');
'a'.shouldNotEqual('b');
'a'.shouldNotBeNull();
'a'.shouldBeA(String);
560行目のstatement.
'a'.shouldEqual('a');
'a'.shouldNotEqual('b');
'a'.shouldNotBeNull();
'a'.shouldBeA(String);
563行目で、
564行目で、
567行目で、