The best way (IMO):
test.add("Expect job not to be done.", function(expect){
expect(job.done).toBe(false);
})
This question has been updated and I post here how I’ve ended doing the tests if someone might need it.
Original question below…
I know it might not even be possible but I will clearly explain why and how I want it to work:
Why?
I want to make some tests that doesn’t fail when something is missing and I wouldn’t want to test everything if exists or not.
Example:
expect('Something', existingClosureVar.notExistingProperty.toString()).toBe('ho ho');
This will throw an error like this: TypeError: Cannot call method ‘toString’ of undefined.
To work around this is quite easy, but also a pain!
if(existingClosureVar && existingClosureVar.notExistingProperty && existingClosureVar.notExistingProperty.toString){
expect('Something', existingClosureVar.notExistingProperty.toString()).toBe('ho ho');
}
Well, but if it doesn’t exist I’m not even noticed about the failing test! Maybe some more detailed workaround might exists but would make this code bigger and bigger, when all I want is a simple thing, which should be the shortest thing possible.
How should work instead?
expect('Something', 'existingClosureVar.notExistingProperty.toString()').toBe('ho ho');
Somehow the expect function should have access to the variable local to the closure in order to make this work. It will run the string in a try-catch context and if fails something will be failing the test too.
Exact thing I want:
var callingFn = function(){
var a = 99;
// remember, the eval can't sit here, must be outside
var evalString = 'console.log(a)'; // should print 99
expect(this, evalString); //
}
var expect = function(context, evalString){
var fn = function(){
eval(evalString)
}
fn.call(context);
}
new callingFn(); // creates a this in the callingFn that is not the window object
It works if I supply a context, but…
that would require me to use the “this.” notation to get a variable. As a lot of functions are async and the context of the functions is not maintained (could be maintained, but more work is needed) (or we could use a closure variable to keep the context variables).
Example:
var callingFn = function(){
var context = {b: 1};
var evalString = 'b'; // prints 1
expect(context, evalString)
}
var expect = function(context, evalString){
var fn = function(){
console.log(eval('this.' + evalString))
}
fn.call(context);
}
callingFn()
One ugly solution I found:
var callingFn = function(){
// setup the context
var context = {};
// give access to the local closure
context.fn = function(evalString){
console.log(eval(evalString))
}
var a = 99;
var evalString = 'a'; // should print 99
expect(context, evalString);
}
var expect = function(context, evalString){
context.fn(evalString);
}
callingFn()
Workable solution but still too much verbose:
And also I must put 4 lines before the code, but here it is Click here to open the Fiddle example:
var callingFn = function(expect){
// give access to the local closure
expect.fn = function(evalString){
try {return [undefined, eval(evalString)];
}catch(e){return [e, undefined];}
}
var a = {hey: 1, b: {c: 99}};
console.log(expect('a.b.c', 99)); // true
console.log(expect('a.b.c.d.f', 99)); // return the error
console.log(expect('a.b.c', 44)); // false
console.log(expect('a.hey', 1)); // true
}
var expect = function(evalString, target){
var result = expect.fn(evalString);
var err = result[0];
var output = result[1];
if(err){
return err.stack;
}else{
return output === target;
}
}
callingFn(expect)
Shared context:
var callingFn = function(expect){
// give access to the local closure
var context = {};
expect.context = context;
context.a = {hey: 1, b: {c: 99}};
console.log(expect('a.b.c', 99)); // true
console.log(expect('a.b.c.d.f', 99)); // return the error
console.log(expect('a.b.c', 44)); // false
console.log(expect('a.hey', 1)); // true
}
var expect = function(evalString, target){
var fn = function(evalString){
try {return [undefined, eval('this.' + evalString)];
}catch(e){return [e, undefined];}
}
var result = fn.call(expect.context, evalString);
var err = result[0];
var output = result[1];
if(err){
return err.stack;
}else{
return output === target;
}
}
callingFn(expect)
Local var + string:
var callingFn = function(expect){
// give access to the local closure
var a = {hey: 1, b: {c: 99}};
console.log(expect(a, '.b.c', 99)); // true
console.log(expect(a, '.b.c.d.f', 99)); // return the error
console.log(expect(a, '.b.c', 44)); // false
console.log(expect(a, '.hey', 1)); // true
}
var expect = function(object, evalString, target){
var fn = function(evalString){
try {return [undefined, eval('object' + evalString)];
}catch(e){return [e, undefined];}
}
var result = fn(evalString);
var err = result[0];
var output = result[1];
if(err){
return err.stack;
}else{
return output === target;
}
}
callingFn(expect)
// examples are for Google Chrome, it might not work in other browsers.
In a word, no. You simply cannot access a variable in a closure from
evalcode.Functions created with
evalor theFunctionconstructor always run in the global scope (or in strict mode, undefined scope). There is no way to avoid this.Furthermore, you cannot access a function’s scope from outside the function (or a function inside of that function). This is because JavaScript has static scope; scope is fixed at the time of declaration. A function cannot change its scope chain.
If you just want your test to fail if a property is undefined, the simplest way might be:
Or use a callback whose invocation is wrapped in a
try.expectcan use the callback’s return value, or allow you to pass a simple value if you know that the identifier in question will always be defined.So you can call
expecteither way: