Run a test in Jest only if certain condition are met - javascript

I want to run certain tests depending on certain conditions. If the condition is met then run, otherwise skip the test. I want to do that before being in the test (or maybe jest provide a function to cancel a test inside it? So that it doesn't throw an error)
You can see here what I have :
The setup: I'm testing different crypto-wallet for a DEX, so I have a battery of tests that runs for all those wallets. By design, some features are on some wallets and others don't have them. Here I'm trying to sign a transaction running on an EVM chain different from Ethereum (AVAX here). This will not work on all wallets (normal) like ethwallet, but will for others. I want to test only those that I know support this feature.
My problem: wallet is initialized in beforeAll, and I don't have access to this object outside test() (the expected log is k2, k1 is out of scope and return gibberish). How can I access it then and know if I run the test or skip it? You can see that I tried to implement testif() but unfortunately not working because I have not the good value outside of test().

According with this Github issue you can do something like:
const runTest = wallet ? it : it.skip
runTest( "testName", async () => {
// ...
}
And the code will execute it or it.skip.

Related

Jest testing, make the variable accessible for all test files

The thing is that I have some preparations to do before starting tests. I've got several test files and each of them requires rabbit connection to be opened before each test file, and close it after the ending of all tests in every file. The thing is that I've got to duplicate code in test files to open and close connection. How can deal with connection in one file. I am using TypeScript so several solutions as jest-environment-node, jest-set are not helpful in this case. Also there is a chance that I can solve this proble using setup files as:
"setupFiles": [
"./src/inttests/partial/mocks/setup.ts"
],
And write in setup file something like:
let channel: PubChannel;
beforeAll(() => {
channel = new PubChannel('amqp://localhost', true);
exports.ss = new SystemService(channel); // or using globals but it doesn't actually work in both cases.
});
afterAll(async () => {
await channel.closeConnection();
}
);
The thing is that I need ss variable to use in tests in order to call some functions. But I don't know how to make them available from setupt.ts file as I can't use export kew word because the value has been modified inside beforeAll scope. Is there any chance to solve this ridiculous problem?

How to correctly put test suites in functions in mocha?

I'm fairly new to mocha, I've been working with it over the summer to write dynamic test cases for a feature in a web application. Basically I can send a request and get all the possible values I can send. So I've been using that to loop through and test everything. To make that easier I created a function with the test suite and a few variables that should let it run all the tests. It kind of works, by that I mean it correctly runs all the tests. However, it waits till after everything else has run for some reason, which causes some issues with what I'm trying to do.
I've simplified what I'm trying to do down to some really basic code:
function test(){
//Testing suite to test through a certain functionality using different variables
describe('inner',function(){
it('test3',function(){
console.log('\ninner test');
});
});
}
function entityLoop() {
describe('outer',function(){
//Get field values from an http request
it('test1',function(){
console.log('\ntest1');
});
it('test2',function(){
console.log('\ntest2');
});
//This MUST run after the other two tests, as those tests get the values needed to run the following tests
//I know it's typically bad practice to make dependent tests, but that's how we're dynamically creating the tests
after(function(){
//Use field values in a testing suite
console.log('after test');
test();
})
})
}
describe("Overall loop",function(){
//Need to loop through a testing suite, changing some variables each time
for(var i = 0; i < 5;i++){
entityLoop();
}
});
And here's the output that I get from it:
test1
test2
after test
test1
test2
after test
test1
test2
after test
test1
test2
after test
test1
test2
after test
inner test
inner test
inner test
inner test
inner test
Process finished with exit code 0
I don't understand why 'inner test' is outputted 5 times in a row at the end, instead of right after 'after test' each time. Any input is greatly appreciated.
Your code is calling describe and it from inside an after hook. It does not crash the test suite but it is not a usage pattern that is explicitly supported by Mocha. So it does not do what you expect it to do.
You get the results you are getting because describe(name, fn) does this:
Create a test suite named name. Add it to the suite that is currently being defined. Push the new suite on the suite stack. Call fn. Pop the suite stack.
The "suite that is currently being defined" is the one at the top of the suite stack. Mocha maintains a stack of suites. It initially contains a top-level implicit suite that Mocha creates to contain all tests. (So you could have a test file that does not use describe at all and it would still work because all the tests would be defined on this implicit top-level suite.)
And it(name, fn) does this:
Create a test named name and with callback fn. Add it to the suite currently being defined.
If you trace mentally what is going on with your code when it runs, you'll find that when the code in after runs, the "suite currently being defined" is the top-level implicit suite that Mocha creates by default to contain all tests. (It has no name. If you add after(function () { console.log("after overall loop"); }) to your top level describe, you'll see that all the inner test outputs appear after the after overall loop output.)
So your after hook is adding new tests to the top-level implicit suite. They necessarily run after all your tests because they are added at the end of the suite.

Disable Jasmine expectation, like xdescribe or xit?

On the Jasmine website I see that we can disable suites by xdescribe or individual specs by xit. Is there a way to disable only an expectation (like xexpect)?
The reason why I'm asking this is because I'm writing e2e tests with Protractor and in our continuous integration we don't yet (if ever) have access to the database, though locally we can run real end to end tests with access to the database, for example.
I would like to mark individual expectations as optional, depending on a configuration or environment variable. It would be nice to make a switch once, and then create a wrapper around expect, that only fails if we are running the tests locally (with access to the database).
So for example I can create a new spec family:
if (process.env.DB_AVAILABLE) {
dbit = it;
} else {
dbit = xit;
}
and write specs that depend on database connection as following:
dbit('creates new user', function () {});
Is there a way to do the same with expect (e.g. dbexpect)?
If there is something fundamentally wrong with my approach, don't hold it back and let me know.
You could create your own xexpect by implementing all the methods/properties with an empty function:
var xexpect = function() {
return xexpect;
};
Object.getOwnPropertyNames(jasmine.Expectation.prototype).forEach(function(name){
xexpect[name] = xexpect;
});
Object.defineProperty(xexpect, 'not', {get: xexpect});
Usage :
xexpect(1).toBeGreaterThan(2);
xexpect(true).not.toEqual(true);

Global beforeEach and afterEach in protractor

In each spec I have beforeEach and afterEach statements. Is it possible to add it somehow globally to avoid code duplication between specs ?
Purpose of beforeEach() and afterEach() functions are to add a block of repetitive code that you would need to execute every time you start or complete executing each spec(it). There are other ways to add generalised code to avoid code repetition, here are few -
If you have a piece of code that you would require to run only once before starting a test suite(describe), then you can use beforeAll() and afterAll() functions that jasmine provides.
If you want to run a piece of code that you want to run only once when the execution starts before starting all the test scripts, then add it in your onPrepare() and onComplete() function.
If you want to add a piece of code that should run even before protractor has started instantiating itself or after it has shut itself down, then use beforeLaunch and afterLaunch.
So it all depends on the scenario that you want to use them in. Hope it helps.
My team has the same desire, to run bits of boilerplate code at the start of every test file. From the discussion here, it doesn't sound like there are hooks to globally add to the beforeEach(), afterEach(), etc.
However, we do use the onPrepare() function to abbreviate the amount of before/after boilerplate code that gets repeated in each spec file. Below is a beforeAll() example, but the pattern could be used for beforeEach()/afterEach(). In this case, we're setting up test users in the database with a DataSeeder class, which we do in the outer-most describe() block in every spec file. (I'm also leaving in my catchProtractorErrorInLocation pattern, because it's super useful for us.)
In protractor.conf.ts add boilerplate code to browser.params object.
onPrepare: function () {
...
const browser = require('protractor').browser;
// Define the ConsoleHelper & DataSeeder instances, which will be used by all tests.
const DataSeeder = require('./e2e/support/data-seeder.js');
browser.params.dataSeeder = new DataSeeder();
browser.catchProtractorErrorInLocation = (error, location) => {
throw new Error(`Error in ${location}\n ${error}`);
};
browser.catchProtractorErrorInBeforeAll = (error) => browser.catchProtractorErrorInLocation(error, 'beforeAll()');
// Return a promise that resolves when DataSeeder is connected to service and ready to go
return browser.params.dataSeeder.waitForConnect();
},
With that in place, we can easily do beforeAll() setup code in an abbreviated set of lines.
beforeAll(() => {
return browser.params.dataSeeder.createTestUsers()
.catch(browser.catchProtractorErrorInBeforeAll);
});
You obviously need to do different things in your setup, but you can see how the pattern can apply.

How to properly test an AngularJS Controller Function

We just started implementing jasmine tests in our AngularJS project and I have a question:
We want to test this controller function:
$scope.deleteClick = function () {
$scope.processing = true;
peopleNotesSrv.deleteNote($scope.currentOperator.operatorId, $scope.noteId, $scope.deleteSuccessCallback, $scope.deleteErrorCallback);
};
We wrote this following test:
it('deleteClick should pass proper parameters to peopleNoteSrv', function () {
$controllerConstructor('PeopleNoteEditCtrl', { $scope: $scope });
$scope.noteId = 5;
expect(function () { $scope.deleteClick(); }).not.toThrow();
});
This test makes sure that when we call the $scope.deleteClick() function that $scope.processing is set to true and that the call to peopleNotesSrv doesn't throw any errors because of invalid arguments. We are testing the two callback functions in separate tests.
Should we be testing that the peopleNotesSrv.deleteNote function was called so the test is more explicit? The way this test is written right now it doesn't really tell someone what the deleteClick() function does under the hood and that seems to be incorrect.
Ask yourself what you'd do if you had developed it using TDD. It pretty much goes the direction Sam pointed out, but here are some examples:
Controller Tests
start writing a test which would expect a deleteClick to exist.
Expect deleteClick to setup the loading state (check for processing = true)
Test whether a service is injected into the controller (peopleNotesSrv)
Check whether deleteClick calls the service (as already mentioned via spies)
Verify that $scope.noteId and the other $scope.params are present and set
This is as far as it relates to the Controller. All the criteria whether it fails or throws errors etc. should be tested in a Service.spec. Since I don't know your service in detail here some examples
Service Tests
Ensure deleteNote exists
Check what happens if wrong number of arguments (less or more) are supplied
Make some positive tests (like your noteId = 5)
Make some negative tests
Ensure callbacks are properly called
... and so on.
Testing for validity in controllers doesn't make a lot of sense because than you'd need to do it for every Controller you have out there. By isolating the Service as a separate Unit of Test and ensure that it fulfills all the requirements you can just use it without testing. It's kinda the same as you never would test jQuery features or in case of Angular jQLite, since you simply expect them to do what they should :)
EDIT:
Make controller tests fail on service call
Pretty easy lets take this example. First we create our Service Test to ensure that the call fails if not the proper number of arguments is supplied:
describe('Service: peopleNoteSrv', function () {
// load the service's module
beforeEach(module('angularControllerServicecallApp'));
// instantiate service
var peopleNoteSrv;
beforeEach(inject(function (_peopleNoteSrv_) {
peopleNoteSrv = _peopleNoteSrv_;
}));
it('should throw error on false number of arguments', function () {
expect(function() { peopleNoteSrv.deleteNote('justOneParameter'); }).toThrow();
});
});
Now to ensure that the test passes lets create the error throwing part in our service method
angular.module('angularControllerServicecallApp')
.service('peopleNoteSrv', function peopleNoteSrv() {
this.deleteNote = function(param1, param2, param3) {
if(arguments.length !== 3)
throw Error('Invalid number of arguments supplied');
return "OK";
};
});
Now lets create 2 demo controllers, FirstCtrl will do it properly, but SecondCtrl should fail
angular.module('angularControllerServicecallApp')
.controller('FirstCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('param1', 'param2', 'param3');
}
});
angular.module('angularControllerServicecallApp')
.controller('SecondCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('onlyOneParameter');
}
});
And both controller as a demo have following test:
it('should call Service properly', function () {
expect(scope.doIt()).toBe("OK");
});
Karma now spits out something like this:
Error: Invalid number of arguments supplied
at [PATH]/app/scripts/services/peoplenotesrv.js:15
at [PATH]/app/scripts/controllers/second.js:13
at [PATH]/test/spec/controllers/second.js:20
Thus you exactly know that you missed to update SecondCtrl. Of course this should work for any of your tests consuming the Service method.
Hope that's what you meant.
I think the answer is that it depends.
There are two cases:
1 - You also have a suite of tests for the peopleNotesSrv service.
In this case I would leave this test as-is or check a few more things around the specific functionality of $scope.deleteClick(), such as if there are any watchers on $scope.processing that do anything specific regarding a .deleteClick() call.
2 - You do not have any tests for all the possible functionality for the peopleNotesSrv service.
In this case I would write a more explicit test that does check that the .deleteNote() actually performed it's job.
In my opinion you should really build tests up and try to not test the same thing in more than one place, as this adds extra work and could produce holes in the tests if you think, "Well I can just test this specific case when it gets called from a specific function that calls it."
What if you ever want to reuse that deletNote() as part of a bigger function in a different place?Then you need to write another test for the same code because it is being called from a different function.
So I would aim for case 1, this way you can write all your tests for that service and then trust that those tests cover the rest of this particular test. If you throw errors on bad input or for failures to actually delete a note, you should trust that other code to test what it was designed to test. This will greatly speed up your test-writing time and increase the chance that your tests cover all the cases. It also keeps all the tests for that service in the same place in your test code.
I think also a good question to start with is what kind of test is this? Unit Test or End-to-End test?
I was assuming it was a Unit Test for my answer, if it was an End-to-End test, then you might want to keep following the function calls to verify everything is happening as you expect.
Here are some links on Unit Tests, End-to-End tests, and a pretty good article about both and Angular.
What's the difference between unit, functional, acceptance, and integration tests? (End-to-End tests can also be called Integration test)
http://www.sitepoint.com/unit-and-e2e-testing-in-angularjs/

Categories

Resources