How can i use soft assertion in Cypress - automation

I have configured soft assert from npm (npm i soft-assert) and now my package.josn has "soft-assert": "^0.2.3"
i want to use function of Soft assert
softAssert(actual, expected, msg, ignoreKeys)
But don't know, what is the exact steps to use it
Example:
When i use soft assertion function in my code, getting following error.
If i use like this
cy.softAssert(10, 12, "expected actual mismatch and will execute next line") : not supported
or if i use different way like
softAssert(10, 12, "expected actual mismatch and will execute next line") : SoftAssert not defined
Can any one tell me how to use this 'softAssert' function in cypress code with some small example?
Now the problem I am facing
it('asserts and logs and fails', () => {
Cypress.softAssert(10, 12, "expected actual mismatch...");
cy.log("text")
Cypress.softAssertAll();
})
I need my code after soft assertion as cy.log("text") to be executed in the same 'it' block but the current test failing the whole 'it' block, without executing 'cy.log("text")' statement.

The soft assertion concept is pretty cool, and you can add it with minimal implementation to Cypress
const jsonAssertion = require("soft-assert")
it('asserts several times and only fails at the end', () => {
jsonAssertion.softAssert(10, 12, "expected actual mismatch");
// some more assertions, not causing a failure
jsonAssertion.softAssertAll(); // Now fail the test if above fails
})
To me, it would be nicer to see each soft assertion failure in the log, so it's possible to add custom commands to wrap the soft-assert functions
const jsonAssertion = require("soft-assert")
Cypress.Commands.add('softAssert', (actual, expected, message) => {
jsonAssertion.softAssert(actual, expected, message)
if (jsonAssertion.jsonDiffArray.length) {
jsonAssertion.jsonDiffArray.forEach(diff => {
const log = Cypress.log({
name: 'Soft assertion error',
displayName: 'softAssert',
message: diff.error.message
})
})
}
});
Cypress.Commands.add('softAssertAll', () => jsonAssertion.softAssertAll())
//-- all above can go into /cypress/support/index.js
//-- to save adding it to every test (runs once each test session)
it('asserts and logs but does not fail', () => {
cy.softAssert(10, 12, "expected actual mismatch...");
cy.log('text'); // this will run
})
it('asserts and logs and fails', () => {
cy.softAssert(10, 12, "expected actual mismatch...");
cy.log('text'); // this will run
cy.softAssertAll();
})

Related

Truffle Chai Assertion Error with Truffle Unit Cases

Problem: facing an issue with the below Truffle test case while trying to Implement the ERC20 token in my contract.
contract("Token Test", async(accounts) => {
const[deployerAccount, recipient, anotherAccount] = accounts;
it("All tokens should be in deployer's account", async() => {
let instance = await Token.deployed();
let totalSupply = await instance.totalSupply();
// let balance = await instance.balanceOf(accounts[0]);
// assert.equal(balance.valueOf(), totalSupply.valueOf(), "The Balance Was not same");
expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(totalSupply);
});
it("Is possible to send tokens between accounts", async() => {
const sendToken = 1;
let instance = await Token.deployed();
let totalSupply = await instance.totalSupply();
expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(totalSupply);
expect(instance.transfer(recipient, sendToken)).to.eventually.be.fulfilled;
expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(totalSupply.sub(new BN(sendToken)));
expect(instance.balanceOf(recipient)).to.eventually.be.a.bignumber.equal(new BN(sendToken));
});
it("Should not be able to send more token than available with owner", async() => {
let instance = await Token.deployed();
let balanceOfDeployer = await instance.balanceOf(deployerAccount);
console.log(balanceOfDeployer+ " : " + await instance.balanceOf(deployerAccount));
// expect(instance.transfer(recipient, new BN(balanceOfDeployer+3))).to.eventually.be.rejected;
expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(balanceOfDeployer);
});
});
Description: When I am trying to execute test cases 2 and 3 at the same time it is failing with the below error:
"before all" hook: prepare suite for "Should not be able to send more token than available with owner":
Uncaught AssertionError: expected '1000000000' to equal '999999999'
+ expected - actual
-1000000000
+999999999
at /project_dir/node_modules/chai-as-promised/lib/chai-as-promised.js:302:22
at processTicksAndRejections (internal/process/task_queues.js:95:5)
UnhandledRejections detected
Promise {
<rejected> AssertionError: expected '1000000000' to equal '999999999'
at /project_dir/node_modules/chai-as-promised/lib/chai-as-promised.js:302:22
at processTicksAndRejections (internal/process/task_queues.js:95:5) {
showDiff: true,
actual: '1000000000',
expected: '999999999',
operator: 'strictEqual',
uncaught: true
}
However when I try to execute both test cases independently (commenting test case 2 keeping 3, and vice-versa). They work fine and are completed without any error.
Need help understanding what am I missing here and how to fix it.
Things I have tried:
Initially, I thought that this is might be happening due to state change in the variable in test case 2. So I moved test case 3 to a new contract(). But I was still facing the same issue. However, this should not be happening as contract() provides a clean room environment before executing every contract test.
P.S.: I am initiating the value of totalSupply to 1000000000 in the constructor of Contract.
Added await before every expect() where I was trying to communicate with the contract. This helped in resolving the issue.

Chai expect "not.to.throw" not catching unexpected error

I'm testing a function using Chai's expect ... not.to.throw pattern (v4.3.4):
describe('parseInt', () => {
it('does not throw TypeError', () => {
// foo is not defined, so a ReferenceError is thrown... somewhere?
expect(() => parseInt(foo)).not.to.throw(TypeError);
});
});
The closure throws an error, but I don't see this error anywhere in the output (running mocha). The test passes without the slightest indication of the problem inside the closure.
I expect to at least see the error in the console, even if the tests pass.
This answer says that such errors must be run like so:
// wrapping describe/it are elided
try {
expect(() => parseInt(foo)).not.to.throw(TypeError);
} catch (e) {
expect(e).to.be.a(ReferenceError); // should PASS, and does
}
This passes, but doesn't appear to be testing the second condition. I can flip it and the test still passes:
// wrapping describe/it are elided
try {
expect(() => parseInt(foo)).not.to.throw(TypeError);
} catch (e) {
expect(e).not.to.be.a(ReferenceError); // should FAIL, but does not
}
What am I missing?
Edit
I think the behavior you are observing is actually expected. The original post you referenced may be misleading.
Consider the following test:
// describe/it wrappers have been redacted
expect(()=>parseInt(foo)).to.not.throw(TypeError)
Should this test pass or fail? Intuitively it should pass since the assertion is met: a TypeError was not thrown.
Again for the following test, should 'hello there' be logged to the console?
// describe/it wrappers have been redacted
try {
expect(() => parseInt(foo)).to.not.throw(TypeError);
} catch (e) {
console.log('hello there')
}
If yes, then the statement expect(() => parseInt(foo)).to.not.throw(TypeError), must throw.
And for it to throw, the assertion - not throw a TypeError - must not be met, but we have already established that the assertion is met, and so no exception is thrown.
Therefore, all code in the catch block is unreachable.
This is why the second assertion in your original example is never executed. It will only be reached if the code somehow threw a TypeError and caused the assertion to fail.
Possible solution
For the approach you are taking, I think it could work if there was a way to tell mocha/chai to still go ahead and propagate the error that is thrown by the target function even if the assertion is met. I am unsure of if there is a way to do this, though.
Alternative approach
Alternatively, I think you can get the same (or similar) outcome for the test you have in mind by doing this instead:
// describe/it wrappers have been redacted
const myFunc = () => parseInt(foo)
expect(myFunc).to.not.throw(TypeError)
expect(myFunc).to.throw(ReferenceError)
Here are the assumptions underlying the above test:
You are expecting an error
The error should under no circumstances be a TypeError
The error thrown should always be a ReferenceError.
In case these assumptions do not match what you are currently testing for, or if my alternate approach does not satisfy your needs, kindly let me know!
PS, I used mocha: "^9.1.0" and chai: "^4.3.4" for these tests.

Why jest expects an anonymous function to catch an error?

I have not comprehended what's the reason behind the fact that expects a curried function when I want to test the output of the error message. If it was going to be a return value a direct call to the function leads to the value to be tested correctly in .toBe
function calculateMedian({numbers}) {
if (Array.isArray(numbers) && numbers.length === 0) {
throw new Error('Cannot calcuate median without any numbers');
}
}
However, if i were to test the following snipped without the anonymous function the test will simply fail. What is the reason behind it?
Passing Test
it('should throw an error when given an empty array', () => {
expect(() =>
calculateMedian({
numbers: [],
}),
).toThrow('Cannot calcuate median without any numbers');
});
Failing Test
it('should throw an error when given an empty array', () => {
expect(calculateMedian({numbers: []})
).toThrow('Cannot calcuate median without any numbers');
});
expect and toThrow are just function calls. So, for the assertion on exception to work, the thing you pass as expect argument needs to allow for execution controlled by the testing framework.
The flow is similar to:
expect() saves the lambda as variable
toThrow() executes the saved variable in a try/catch block and tests the caught exception.
The way you would do it without the toThrow method would be similar to:
try {
calculateMedian({numbers: []};
fail();
} catch (err) {
expect(err.message).toBe('Cannot calcuate median without any numbers')
}
If, instead of passing a lambda/function you simply invoke the function, the error will be thrown before program control gets to the toThrow method. The test will fail because of the thrown error.

handling error from Angular httpClient retryWhen block

I have a pretty simple http call happening. Upon error I want the request to retry 3 times with a three second delay between retries. I have worked out a solution that is close:
return this.http.put<string>(URL,
value).retryWhen(err => {
return err
.delay(3000)
.take(3)
.concat(Observable.throw("error occurred"));
})
.catch(err => this.handleHttpError(err)) ;
The client retries three times like I expect. However, I don't know how to throw the final error in such a way that my error handler (which normally expects an HttpResponse argument) can process the error.
Once I have taken(3) how can I get the final error, and convert it to an HttpResponse to send to my handler? Or am I looking at this the wrong way?
I need to know, at the end of the day, the HttpResponse that accompanied the error(s). When I throw the err from the retryWhen at the concat function that doesn't seem to accomplish it.
I am betting this is a pretty common thing to do, but being newer to Angular 5 and react I think I am just missing the boat.
You can use concatMap to count how many times you've tried to resubscribe and according to that send next or error notifications (this means re-throwing the error in the inner observable that is propagated further).
Observable.throw(42)
.retryWhen(err => err
.do(console.info) // remove, prints 42
.delay(3000)
.concatMap((error, index) => {
if (index === 2) {
return Observable.throw("error occurred"); // or Observable.throw(error);
}
return Observable.of(null);
})
)
// .catch(err => handleHttpError(err))
.subscribe(
v => console.log('next', v), // not called
e => console.log('error handler', e),
);
This prints the following output:
42
42
42
error handler: error occurred
See live demo: https://stackblitz.com/edit/rxjs5-jt5ald
For anyone who runs into this, I was able to capture the httpErrorResponse by slightly changing the return:
first I added a local var
let httpError: HttpErrorResponse = null;
then I modified the return:
return error
.do(err => {if (err instanceof HttpErrorResponse) {httpError = err; }})
.delay(3000)
.take(5)
.concat(Observable.throw(error));
})
this allows me to cache the last http error response. I then look for this in the catch and work accordingly. Seems to be working just fine.

Chai-As-Promised is eating assertion errors

I'm using chai-as-promised + mocha for writing some selenium-webdriver tests. Since webdriver extensively uses promises, I imagined it would be better if I used chai-as-promised for those type of tests.
The problem is that when the tests fail, the error is not being caught properly by mocha, and it just fails without outputting anything.
Sample code:
it 'tests log', (next) ->
log = #client.findElement(By.css("..."))
Q.all([
expect(log.getText()).to.eventually.equal("My Text")
expect(log.findElement(By.css(".image")).getAttribute('src'))
.to.eventually.equal("/test.png")
]).should.notify(next)
According to documented behaviour, chai-as-promised should pass the errors along to mocha when the expectation fails. Right?
As a variation,
I've also tried these, but to no avail:
#2
# same, no error on failure
it 'tests log', (next) ->
log = #client.findElement(By.css("..."))
Q.all([
expect(log.getText()).to.eventually.equal("My Text")
expect(log.findElement(By.css(".image")).getAttribute('src'))
.to.eventually.equal("/test.png")
]).should.notify(next)
#3
# same, no error shown on failure
it 'tests log', (next) ->
log = #client.findElement(By.css("..."))
expect(log.getText()).to.eventually.equal("My Text")
.then ->
expect(log.findElement(By.css(".image")).getAttribute('src'))
.to.eventually.equal("/test.png").should.notify(next)
#4
## DOES NOT EVEN PASS
it 'tests log', (next) ->
log = #client.findElement(By.css("..."))
Q.all([
expect(log.getText()).to.eventually.equal("My Text")
expect(log.findElement(By.css(".image")).getAttribute('src'))
.to.eventually.equal("/test.png")
])
.then ->
next()
.fail (err) ->
console.log(err)
.done()
I think you're having two different issues:
First, you need to return a promise at the end of your tests (e.g. Q.all([...]); in your first test should be return Q.all([...]).
Second, I found that chai-as-promised doesn't seem to work unless used with mocha-as-promised.
Here's an example of mocha-as-promised with two assertions that will cause the test to fail.
/* jshint node:true */
/* global describe:false */
/* global it:false */
'use strict';
var Q = require('q');
require('mocha-as-promised')();
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;
describe('the test suite', function() {
it('uses promises properly', function() {
var defer = Q.defer();
setTimeout(function() {
defer.resolve(2);
}, 1000);
return Q.all([
expect(0).equals(0),
// the next line will fail
expect(1).equals(0),
expect(defer.promise).to.eventually.equal(2),
// the next line will fail
expect(defer.promise).to.eventually.equal(0)
]);
});
});