`
othella
  • 浏览: 83192 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
社区版块
存档分类
最新评论

用于前端行为测试的JsMock

阅读更多
JsMock用于测试行为,接口的交互测试。
JsMock的地址: http://jsmock.sourceforge.net/

先看个官网上的最简单的例子

function Worker() {
	this.getValue = function() { /* ... */}
	this.isFinished = function() {/* ... */}
}
//Fixture
function doWork(worker) {
	if(worker.isFinished()) {
		return worker.getValue();
	} else {
		return null;
	}
}
//Tests
function test_doWork() {
	//1. Create a mockControl object
	var mockControl = new MockControl(),
		//2. Use mockConrol create a mock object for the Class
		workerMock = mockControl.createMock(Worker);
	//3. Set the expects for methods of the Class
	workerMock.expects().isFinished().andReturn(true);
	workerMock.expects().getValue().andReturn('hello');
	//4. Use mock as the instance of the Class, run it.
	var result = doWork(workerMock);
	
	//assertEquals('hello', result);
	
	//5. verify the test
	mockControl.verify();
}

function test_doWork_null() {
	var mockControl = new MockControl();
	workerMock = mockControl.createMock(Worker);

	workerMock.expects().isFinished().andReturn(false);

	var result = doWork(workerMock);
	
	<span class="comment">//assertNull(result);</span>
	mockControl.verify();
}

大概的流程如图:


JsMock功能简单却很实用。关键是看你如何运用。首先前端开发人员要有意识的做到UI/Logic分层。让UI部分的code仅仅只做UI显示用。任何逻辑相关的代码可以分离出来。目前前端UI测试没有好的工具,只能靠人肉。但是逻辑部分的code我们完全可以做好单元测试。利用JsUnit和JsMock做状态,行为测试。保证我们的代码质量。

JsMock的方法:
  • createMock(Object2Mock) - Create the mock object
  • andReturn(value) - Set the return value on one method for some control logic's test
  • andThrow(throwMsg) - Let the method throw a exception you set
  • andStub(block) - The parameter should be a function. You can use it to get the arguments of the method
  • addMockMethod(method) - Add extra method into mock object
  • reset() - Clear the excepts
  • verify() - Verify the test


再来贴下JsMock的部分源码:

	/**
	 * Create the mock for object/class
	 * @param objectToMock object/function
	 * @return mock object
	 */
	createMock: function(objectToMock) {
		var mock = {
			calls: [],
			expects: function() {
				this.__recording = true;
				return this;
			},
			__recording: false
		};
		mock.expect = mock.expects;
		
		if(objectToMock != null) {
			//if the param is a function - a Class
			if( typeof(objectToMock) == 'function' ) {
				//create methods for class's static methods.
				this.__createMethods(objectToMock, mock);
				//new an instance of the class, and create methods for it
				this.__createMethods(new objectToMock(), mock);
			} else if( typeof(objectToMock) == 'object') {
				//if the param is an object, creat methods for it
				this.__createMethods(objectToMock, mock);
			} else {
				throw new Error("Cannot mock out a " + typeof(objectToMock));
			}
			
		}
		
		var self = this;
		//add addMockMethod into mock object
		mock.addMockMethod = function(method) {
			self.__createMethod(self, mock, method);
		};
		
		return mock;
	}
.....

/**
	 * @private create methods of the object/class into mock
	 * @param object - the object you want to mock it
	 * @param mock - the target mock object
	 */
	__createMethods: function(object, mock) {
		for( property in object ) {
			//if the property is a public method, create method into mock
			if( this.__isPublicMethod(object, property) ) {
				this.__createMethod( this, mock, property );
			}
		}
	},
	/**
	 * @private create the method into mock
	 * @param control - mockControl object
	 * @param mock - the target mock object
	 * @param method - the method need add into mock object
	 */
	__createMethod: function(control, mock, method) {
		//create the property of mock for the method
		mock[method] = function() {
			//If you call expect() first, the __recording value is true
			if( mock.__recording ) {// expect
				control.__lastMock = mock;
				control.__lastCallName = method;
				//add this method into expected calls
				control.__expectationMatcher.addExpectedMethodCall( mock, method, arguments );
				//clear the flag
				mock.__recording = false;
				//return the control object
				return control;
			} else {// If it's real call
				//add this method into actual calls
				control.__expectationMatcher.addActualMethodCall( mock, method, arguments );
				//read mock calls to get the array under the method
				if( mock.calls[method] != null) {
					//get the latest value, popup
					returnValue = mock.calls[method].shift();
					//If the value is a function, apply it
					if( typeof(returnValue) == 'function') {
						return returnValue.apply(this, arguments);
					}
				}
			}
		};
	}


今天我给同事分享了这个,有人问我能否用JsMock模拟Ajax call的数据。我觉得可以定义一个测试用的Ajax类。加上用setInterval/setTimeout来模拟异步call的特性。关键看你自己如何运用。

我个人觉得JsMock非常的实用。
1. 节省时间
2. 保证代码质量
3. 不会被别人block住

这里有人会说要写额外的代码,怎么叫节省时间。可能你在写这些代码的时候是花了时间。但是比起后面要花在跟别人接口整合,和调试问题的时间,这点时间完全是值得的。

保证代码质量这块就不多说了。这是所有测试的目标。你可以写尽可能多的测试case,保证你的代码质量。

Mock即模拟的意思。即使在你开发时候你所依赖的接口还没有好,你可以把对象Mock起来,用andReturn/andThrow等方法来控制返回结果,控制你自己代码的行为。

在以后的代码我会尽量按照这个方向来的。
  • 大小: 36.9 KB
分享到:
评论
1 楼 cuixiping 2010-12-21  
这个东西不错。
最近很少更新啊

相关推荐

Global site tag (gtag.js) - Google Analytics