The mock object library inspired by Python's mock implemented by Proxy objects.
npm install object-mocker
import {mock, getRegistry, getDeepChildHandler} from "object-mocker"; import {afterEach} from "selenium-webdriver/testing";
function getValueFromSource(source) {
return source.loadValue();
}
const source = mock();
const result = getValueFromSource(source);
afterEach(() => {
// clear registry after each test
getRegistry().clear();
})
expect(result).toBe(getDeepChildHandler(source, "loadValue").report.getLastCall().returnValue);
// clear registry after test
getRegistry().clear();
Use only specialized functions to get nested mock objects instead of direct access by a mock object! Accessing a nested object directly create new history records and this can break test expectations.
import {mock, getRegistry} from "object-mocker";
const myMock = mock();
const value = myMock.child.value;
// was child accessed?
const registry = getRegistry();
expect(registry.getHandlerByObject(myMock.child).filter(r => r.type == "get" && r.property == "value").length).toBe(1)
// WRONG - GETTING THE HANDLER THIS WAY CAUSE CREATION OF NEW ACCESS RECORD!
import {mock, getRegistry, getDeepChildHandler} from "object-mocker";
const myMock = mock();
const value = myMock.child.value;
// was child accessed?
const registry = getRegistry();
expect(getDeepChildHandler(myMock, "child").filter(r => r.type == "get" && r.property == "value").length).toBe(1)
// OK - THERE IS ONLY ONCE RECORD
NoEmulatedPrototype
(Symbol
) - declare no prototype emulation forinstanceof
operator.ResultValueFactory
((args: any[], handler: Handler) => any
) - signature of factories used for making results of function calls and thenew
operator.Handler
- manage actions done on mock object.Registry
- store pairs of mock objects and their handlers.Report
- hold information about mock object access (get property, set property, ...)
The mock objects are create by the mock(options?: MockOptions)
function. This function has one optional argument. This argument contains configuration of the mock objects.
The MockOptions
object contains only one field at this time. This field is the handlerOptions
containing partial configuration of the Handler
class.
Handlers are responsible for storing usage reports and handling actions done on mock objects.
Configuration of Handler is done by the CommonHandlerOptions
interface:
target
(any?
) - target object used as prototype. Default is aFunction
instance.registry
(Registry
) - the registry where to store child objects.returnValueFactory
(ResultValueFactory
) - factory used for return value when object is called as function.instanceFactory
(ResultValueFactory
) - factory used for create "instance" when object is used with thenew
operator.emulatedPrototype
(any
|NoEmulatedPrototype
) - prototype used for emulation ofinstanceof
operator.parent
(Handler?
) - handler which created this handler.useAutoCreate
(boolean
) - if true, attempt to access undefined property create new mock object and set it to this property. Theundefined
value is returned otherwise.
When mock object is called as a function or used as a constructor by the new
operator, return value must be generated. For this purpose there are some built in value generator factories:
makeUniqueFactory()
- make factory returning unique mock object each time called.makeSingletonFactory()
- make factory returning new value the first time its called and then always return this value.
For custom generators see value generator signature above.
Pairs of handlers and mock objects are stored in a registry. The Registry
API is:
clear(): void
- clear all records in registry.getObjectByHandler(handler: Handler): any
- get mock object paired with given handler.getHandlerByObject(obj: any): Handler
- get handler paired with given object.register(obj: any, handler: Handler): void
- register a new pair of an object and a handler.getObjects(): any[]
- get all registered objects.getHandlers(): any[]
- get all registered handlers.
getRegistry(): Registry
- get an active registry (there is always built in registry set in default).setRegistry(registry: Registry): void
- set new active registry.
Actions, done on a mock object, are stored in reports. Following table shows observed actions and collected information:
Action | Record type |
---|---|
get property | PropertyGet |
set property | PropertySet |
delete | PropertyDelete |
call as function | Call |
use as construction (the new operator) |
Construct |
getHistory()
- return all usage records.isCalled()
- return true if mock object was called at least once, false otherwise.getLastCall()
- return lastCall
record or throw an Error if no call was performed.getCalls()
- return list of all theCall
records.getPropertyGets()
- return list of allPropertyGet
records.getPropertySets()
- return list of allPropertySet
records.getPropertyAccess(propName: string)
- return all records of the property.filter(pred: (record: ObjectAccess) -> bool)
- apply a custom filter to the history of a mock object.clear()
- remove all records from a mock object's history.
type
(str
) - be always theget
property
(str
) - name of the propertyvalue
(any
) - value returned by propertywasDefined
(boolean
) - true if value was defined, false otherwise
type
(str
) - be always theset
property
(str
) - name of the propertyvalue
(any
) - new value of the propertyoldValue
(any
) - old value of the propertycreated
(boolean
) - true if property was not defined before assignment, false otherwise
type
(str
) - be always thedelete
property
(str
) - name of the deleted property
type
(str
) - be always thecall
arguments
(any[]
) - call argumentsreturnValue
(any
) - value returned by the call
type
(str
) - be always theconstruct
arguments
(any[]
) - constructor call arguments
getDeepChildHandler(obj: any, path: string, autoCreate: boolean=true): Handler
- get handler of the nested object. IfautoCreate
is true and handler is missing, the there is missing part of path, this part is created automatically. Example:const nestedHandler(myMock, "child.childOfChild")