How to test if a function asserts in a NEAR smart contract (AssemblyScript)? - nearprotocol

I have a function in my NEAR smart-contract (AssemblyScript) that I want to test. I want to test if the assertion actually happened.
AssemblyScript
foo(id: string): boolean {
assert(id != 'bar', 'foo cannot be bar');
return true;
}
Unit test (as-pect)
describe('Contract', () => {
it('should assert', () => {
contract.foo('bar'); // <-- How to test assertion here
})
});
After running the above test, the console logs says
Failed:
should assert - foo cannot be bar
I know I can return false or throw instead of doing an assert for the above example, and I may do that instead if it makes testing easier.

use toThrow()
Like this:
describe('Contract', () => {
it('should assert', () => {
contract.foo('bar').toThrow('foo cannot be bar');
})
});
you can also use not.toThrow() to test not trowing:
it('should assert', () => {
contract.foo('foo').not.toThrow();
})
});

Related

Turn off clearing aliases in Cypress test

We have a number of variables from the API which are used throughout the tests. The values are saved to aliases at various points.
How can we turn off clearing aliases between test?
If we use one big test this works fine but Cypress by default clears aliases between tests, so as we break down the tests into smaller more manageable units we are breaking the code.
Simple example:
before(() => {
cy.wrap(123).as('alias')
})
it('test1', () => {
// alias is usable here
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
it('test2', () => {
// alias is missing here
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
There's no configuration to turn off alias cleardown.
You would have to create a custom command to do it.
Cypress.Commands.add('keepAliases', function(aliasList) {
if (!aliasList) {
aliasList = Object.keys(this)
.filter(key => !['test', '_runnable', 'currentTest']
.includes(key))
}
aliasList.forEach(key => {
cy.wrap(this[key]).as(key)
})
})
Usage for a single test
before(() => {
cy.wrap(123).as('alias')
})
it('test1', () => {
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
// ✅ test passes
it('test2', () => {
cy.keepAliases()
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
Usage for all tests
before(() => {
cy.wrap(123).as('alias')
})
beforeEach(() => {
cy.keepAliases()
})
it('test1', () => {
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
// ✅ test passes
it('test2', () => {
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
According to cypress doc
aliases and properties are automatically cleaned up after each test
so with current cypress versions your goal is not achievable.
As a workaround you could use cypress.env as a storage for your tests:
Cypress.env(varName, varValue)
and using a test0 to reset all variables before each texts execution:
it('test0', () => {
// clean up all stored aliases
cy.env("var1",null);
cy.env("var2",null);
//...
});

Cypress: Why finicky in handling spec's after hook?

Blocked by a Cypress custom command not executing properly within a spec's after hook when a test failed. Experimented with the following minimal spec examples.
Context
Testing two Cypress custom commands:
cy.cmdLogin(): Standard username password authentication
cy.cmdLogout(): cy.get('logout_button').click()
Spec #1, cy.cmdLogout() in after hook does work
Very simple Cypress tests spec, test cy.cmdLogin() with after hook with cy.cmdLogout(), works perfectly:
context('SPEC Login', () => {
after('AFTER Logout', () => {
if (this.successLogin) {
cy.cmdLogout().then(() => {
cy.log('Logout Success');
});
}
});
it('TEST Login', () => {
cy.cmdLogin().then(() => {
cy.log('Login Success');
this.successLogin = true;
});
});
});
Spec #2, cy.cmdLogout() in after hook does not work
Modified Cypress tests spec, test cy.cmdLogin() with after hook with cy.cmdLogout(), now added a test that forced failure expect(false).to.be.a('boolean').to.be.true;. The after hook cy.cmdLogout() is called and fails:
Verified that Cypress could handle cy.get('logout_button') without failure.
Yet, extending with .click(), cy.get('logout_button').click(), Cypress would throw an error: Can not perform click() on undefined.
context('SPEC Login', () => {
after('AFTER Logout', () => {
if (this.successLogin) {
cy.cmdLogout().then(() => {
cy.log('Logout Success');
});
}
});
it('TEST Login', () => {
cy.cmdLogin().then(() => {
cy.log('Login Success');
this.successLogin = true;
});
});
it('TEST Fail', () => {
expect(false).to.be.a('boolean').to.be.true;
});
});
Spec #3, removed after hook, cy.cmdLogout() moved to another test does work
Modified Cypress tests spec again and removed after hook, test cy.cmdLogin(), test forced failure, and now test cy.cmdLogout(). This works perfectly:
context('SPEC Login', () => {
it('TEST Login', () => {
cy.cmdLogin().then(() => {
cy.log('Login Success');
this.successLogin = true;
});
});
it('TEST Fail', () => {
expect(false).to.be.a('boolean').to.be.true;
});
it('TEST Logout', () => {
if (this.successLogin) {
cy.cmdLogout().then(()=> {
cy.task('log', 'Logout Success');
});
}
});
});
Insight as to why Cypress is finicky with spec's after hook would be very much appreciated
The short answer (for the general situation) is change after() hook to before() hook, but in your case that wouldn't work because this.successLogin will always be undefined inside a before.
You would need to create a cy.isLoggedIn() command or call cy.cmdLogout() unconditionally.
To get after() the logic working on a fail, use
after('AFTER Logout', () => {
cy.cmdLogout()
});
Cypress.on('fail', (error, runnable) => {
cy.cmdLogout()
throw error
})
which is equivalent to this in plain javascript
try {
some-code-that-might-error()
cy.cmdLogout() // does not run if error occurs
} catch (error) {
cy.cmdLogout() // do the logout in error situation
}

How to instruct cypress to do some actions if the testcase is passed?

I try to use aftereach() but it will execute either the testcase is passed or failed. I need to know the context of that should i use for example if condition or what?
For example:
describe('TestSuite', function(){
it('THIS TEST CASE PASSED', function(){
})
it('THIS TEST CASE FAILED', function(){
})
})
I need to make it like this. If the a testcase is passed do the actions
...
...
...
and if the testcase failed do the actions
...
...
...
You didn't give much info about your code, so I will suggest a solution that might not be suited for you. I would use a variable that gets set to "fail" if an assertion fails in the previous test and use that to determine the action in the 2nd test, e.g.
describe('check if first test passed or failed then do A or B', () => {
let result
it('test 1', () => {
cy.request({url: Cypress.env('url')}).its('status').then((status) => {
if (status === 500) {
result = 'fail'
}
})
})
it('test 2', () => {
if (result === 'fail') {
cy.log('Previous test failed, so I did Action A')
Code Action A
}
else
{
cy.log('Previous test passed, so I did Action B')
Code Action B
}
})
})

Mocha .then(done) doesn't work as expected

This question is not about a problem which I can't solve, it is just a curiosity. I'm not very experienced with Mocha, but there's something interesting I've stumbled upon already.
What I want is to use done() to tell Mocha the promise has been resolved.
The following code DOESN'T work:
beforeEach((done) => {
user = new User({ name: 'Dummy' })
user.save()
.then(done)
})
I know I'm passing the result of the user.save() promise to done, but I think it shouldn't be a problem.
Instead this other code works:
beforeEach((done) => {
user = new User({ name: 'Dummy' })
user.save()
.then(() => done())
})
It seems to me that Mocha done() has some kind of control flow which leads to: Error: done() invoked with non-Error: {"_id":"5b65b9d2669f7b2ec0a3d503","name":"Dummy","__v":0}
Is it because done() wants strictly an error as its argument?
Why done() does even care about what I pass to it?
Can you make some example showing why done() argument to be an Error is useful?
Thanks in advance ;)
It is because done() in Mocha only accepts Error argument. In your case, your save() method returns json object not an Error ie new Error('failed save').
If we take a look at mocha test file, we can see that it won't accept other type of arguments.
// https://github.com/mochajs/mocha/blob/master/test/unit/runnable.spec.js#L358
describe('when done() is invoked with a string', function () {
it('should invoke the callback', function (done) {
var test = new Runnable('foo', function (done) {
done('Test error'); // specify done with string/text argument
});
test.run(function (err) {
assert(err.message === 'done() invoked with non-Error: Test error');
done();
});
});
});
But if we see the test when the argument is Error, it works
// https://github.com/mochajs/mocha/blob/master/test/unit/runnable.spec.js#L345
describe('when an error is passed', function () {
it('should invoke the callback', function (done) {
var test = new Runnable('foo', function (done) {
done(new Error('fail'));
});
test.run(function (err) {
assert(err.message === 'fail');
done();
});
});
});
Btw, I suggest that you avoid using done since mocha supports promise by specifying return statement. So, we change the code into
beforeEach(() => {
user = new User({ name: 'Dummy' })
return user.save().then(user => {
// antyhing todo with user
});
});
Hope it helps.

Run function after beforeEach

I need to run a function before my tests, but after all beforeEaches. Is there a way of prioritise beforeEaches, reorder them or monkeypatching something so that I can run a function before test gets executed?
Reason: I want to count how many database calls my tests do, but not include those that beforeEach calls.
Mocha executes the beforeEach hooks that pertain to a test in the order in which it encountered them in your code, so there should not be a need to monkeypatch. For instance:
describe("top", () => {
beforeEach(() => {
console.log("A");
});
describe("down", () => {
beforeEach(() => {
console.log("B");
});
beforeEach(() => {
console.log("C");
// Put your code here.
});
it("test 1", () => {});
it("test 2", () => {});
});
});
The above outputs:
top
down
A
B
C
✓ test 1
A
B
C
✓ test 2

Resources