How to reuse beforeEach/afterEach in Jasmine JS? - javascript

When writing tests with JasmineJS I have many tests that have similar beforeEach/afterEach code.
Is there a way to implement an inheritance model using JasmineJS test suites?
I can group all tests in a single describe but in this case I will end with a single HUGE JS file containing all tests.
I would like to split the tests for each page.
Here is an example:
describe('Services Page', function() {
beforeEach(function() {
login_as_admin()
})
beforeEach(function() {
browser().navigateTo('/services')
})
if('Some test for services page', function() {})
afterEach(function() {
logout()
})
})
describe('Administrators Page', function() {
beforeEach(function() {
login_as_admin()
})
beforeEach(function() {
browser().navigateTo('/administrators')
})
if('Some test for administrators page', function() {})
afterEach(function() {
logout()
})
})

I think this is partially examined in this blog post and also answered here but i'm adding an adapted answer for your example:
Reusable code:
function sharedSetup(startPage) {
beforeEach(function() {
login_as_admin();
browser().navigateTo(startPage);
});
afterEach(function() {
logout();
});
};
How to use it:
describe('Services Page', function() {
sharedSetup('/services');
it('Some test for services page', function() {});
});
describe('Administrators Page', function() {
sharedSetup('/administrators');
it('Some test for administrators page', function() {});
});

If you want to do this for all your suites, you can register a beforeEach or afterEach function in the topSuite:
jasmine.getEnv().topSuite().beforeEach({fn: function() {
//log in as admin
}});
If you only want to apply it on some suites, you can work with sub-suites:
describe("as_admin", function() {
beforeEach(function() {
//log in as admin
});
describe('Services Page',function() {...});
describe('Administrators Page',function() {...});
}

Jasmine does allow you to put beforeEach and afterEach outside of a describe call. In this way you can have setup and teardown that is global for all of your specs. Your logout() call seems like it might be a good candidate for global teardown, and if all of your specs login as an admin, you could move that out to global scope as well.
For things that are used in some, but not all, specs, having a method like your login_as_admin() seems like the best way to consolidate that logic in one place.

Reference: (Pivotal Labs Blog:Davis W. Frank)
He describes collecting shared functionality in a function that is called with parameters for the different individual suites. Calling this function within each suite will execute the common setup/configuration.
As to splitting tests across files; the file with the shared function can either be included within each page with a <script> tag if the tests are browser based, or by a require(...) near the top if the tests are node based. You can then run the tests independently but using that shared setup which is defined only once.

Found in the issues.
So in 3.7.0 afterEach along with other methods got moved out of Env namespace and into Globals.
the call in test.ts should be now:
afterEach(() => {});
that's it.

Related

jasmine angular testing - is it possible to add a attribute on a describe method?

I am new to jasmine testing and coming from a xUnit .Net background.
Is it possible to label a test or a suite of tests in such a fashion:
[SomeAttribute]
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
Does jasmine support any sort of attributes or identifiers? My goal really is to run a describe group of tests twice, with a different setting between test runs. I did not want to duplicate the tests. Is it possible for a test to kick off other tests?
This question is assuming that I am satisfied with duplicating a build step to run the test suit twice, just with a subset of tests for the second run.
Edit: More realistic example of how I would hope to consume it
[Theory]
[TestData(true)]
[TestData(false)]
describe("A suite", function() {
beforeEach(() => {
configureTestBed(/*someHow get input*/);
});
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
What you can do is to define separate functions which will accept parameters from somewhere else in your code.
Something like this would do:
describe('Sample describe', () => {
testFunction(1);
});
function testFunction(param1) {
it('should execute test with params', () => {
console.log(param1);
expect(param1).toBe(1);
});
}

Nightwatch - Determine if tests have passed or failed

I'm trying to integrate my test suite with Saucelabs and to determine if the tests have passed or failed I need to do this myself.
Here is the code for the test that I have (notice I'm using Mocha):
describe('Smoke Test', () => {
describe('Login', () => {
it('Should login', (client) => {
pages.login(client).validLogin(client.globals.users.SMOKE.USERNAME, client.globals.users.SMOKE.PASSWORD);
});
});
after((client, done) => {
client.end(() => {
done();
});
});
});
Is it possible in the after block to know if the test have passed or failed?
From some examples that I found, including Saucelabs example I've seen this line:
client.currentTest.results
However currentTest have only name and method attributes.
Well, this might be late reply for you. Hope this is useful for other viewers.
afterEach will have results. Add afterEach to your example.
Apart from this, you can also get results in runner itself. Explore 'reporter' file in mocha-nightwatch. It is in your node_modules.
..\node_modules\mocha-nightwatch\lib\reporters\json.js
There are events like
runner.on(start ..)
and
runner.on(end..)
these will trigger for each tests. Give a try.

Spyon provider during config phase in angular.js application

I am writing unit tests for an Angular.js application (with karma and jasmine), and I want to test a certain behavior in the CONFIG phase of a module. I would like to confirm that a certain function of a PROVIDER is being called. I thought I could do this with a spy on the provider's method, but gaining access to the provider before the "expect" has proven rather tricky.
Here is some example code:
Module Code (being tested)
var myApp = angular.module('myApp', ['restangular']);
myApp.config(['RestangularProvider', function (RestangularProvider) {
RestangularProvider.setBaseUrl('http://someurl:someport/');
}]);
I've tried various solutions to get a reference to the RestangularProvider and apply a spy to it, and all failed. The closest I was able to get was the code below:
Unit Test Code
describe("Test if setBaseUrl was called", function () {
var RestangularProvider;
beforeEach(module('myApp', function(_RestangularProvider_) {
RestangularProvider = _RestangularProvider_;
spyOn(RestangularProvider, "setBaseUrl").and.callThrough();
}));
it("should call setBaseUrl.", function() {
expect(RestangularProvider.setBaseUrl).toHaveBeenCalled();
});
});
I do actually get the reference to the RestangularProvider, but the "config" function of the module gets called before that, so I think the spy doesn't get set-up.
I did find a post where the author solved a similar situation with a "work around" by testing the configured "service" instead of testing the actual call to the provider's method. In the example above, I would test the Restangular.configuration.baseUrl in my expect instead of testing the actual call to the provider's setBaseUrl method, but this seemed like it would not be adequate in certain situations.
I am rather new to Angular.js so this may simply be a case of being totally clueless as to the whole "testing config phase", so if that's the case, please feel free to set me straight :]
Any suggestions, critiques or pointers?
I finally solved the problem by separating out the module, whose provider I wanted to spy on, into a diferent "beforeEach" block. The altered code is below, but I still would appreciate any comments as to the whole idea of whether or not this is actually an "adequate test".
describe("Test if setBaseUrl was called", function () {
var RestangularProvider;
//Setup the spy.
beforeEach(function () {
module("restangular", function(_RestangularProvider_) {
RestangularProvider = _RestangularProvider_;
spyOn(_RestangularProvider_, 'setBaseUrl').and.callThrough();
});
});
beforeEach(module('myApp'));
it("should call setBaseUrl.", function() {
expect(RestangularProvider.setBaseUrl).toHaveBeenCalled();
});
});
As described by OP above, you do need to get the provider before calling the module you want to test.
However, there's no need to separate it in two beforeEach blocks. You also must call inject() function (even if you have nothing to inject) at the end of the beforeEach block.
describe('Test if setBaseUrl was called', function () {
var RestangularProvider;
//Setup the spy.
beforeEach(function () {
module('restangular', function(_RestangularProvider_) {
RestangularProvider = _RestangularProvider_;
spyOn(_RestangularProvider_, 'setBaseUrl').and.callThrough();
});
module('myApp');
inject();
});
it('should call setBaseUrl.', function() {
expect(RestangularProvider.setBaseUrl).toHaveBeenCalled();
});
});
Source: http://java.dzone.com/articles/unit-testing-config-and-run

Is there an Jasmine JS equivalent of setUpClass?

Is there some trick to run some code at the begining and at the end of a "describe" test suite?
I am looking for something similar to setUpClass/tearDownClass from XUnit
In this example, i want to run "login_as_admin" only once before all tests and "logout" only once after all tests.
Thanks!
Here is the sample code.
/*
Functional tests.
*/
describe('Services Page', function() {
it('setUpClass', function() {
login_as_admin()
})
/*
Before each test make sure we are on the services page.
*/
setup(function() {
browser().navigateTo('/PAGE_UNDER_TEST')
})
it(
'Click on add service will get us to the Add service page.',
function() {
element('#add-service').click()
expect(browser().location().path()).toBe('/services/_add')
})
it(
'Click on edit service will get us to the Edit service page.',
function() {
element('#edit-service').click()
expect(browser().location().path()).toBe('/services/local-manager')
})
it('tearUpClass', function() {
logout()
})
})
There are a few patches to jasmine (1) and (2) which support doing this. However they do not appear to be well maintained. I have moved away from Jasmine to Mocha for this very reason.

Unit test when loading things at app run with AngularJS

I need my app to run some configuration at runtime vi an HTTP endpoint.
I wrote a simple service to do that:
module.factory('config', function ($http, analytics) {
return {
load: function () {
$http.get('/config').then(function (response) {
analytics.setAccount(response.googleAnalyticsAccount);
});
}
}
});
Next, I call this module in a run block of my app module:
angular.module('app').***.run(function(config) {
config.load();
});
All is working well when the app is running but in my unit tests, I get this error: "Error: Unexpected request: GET /config"
I know what it means but I don't know how to mock it when it happens from a run block.
Thanks for your help
EDIT to add spec
Calling this before each
beforeEach(angular.mock.module('app'));
Tried this to mock $httpBackend:
beforeEach(inject(function($httpBackend) {
$httpBackend.expectGET('/config').respond(200, {'googleAnalyticsAccount':});
angular.mock.module('app')
$httpBackend.flush();
}));
But got:
TypeError: Cannot read property 'stack' of null
at workFn (/Users/arnaud/workspace/unishared-dredit/test/lib/angular/angular-mocks.js:1756:55)
TypeError: Cannot read property 'stack' of null
at workFn (/Users/arnaud/workspace/unishared-dredit/test/lib/angular/angular-mocks.js:1756:55)
TypeError: Cannot read property 'stack' of null
at workFn (/Users/arnaud/workspace/unishared-dredit/test/lib/angular/angular-mocks.js:1756:55)
EDIT since update to AngularJS 1.0.6
Since I've updated to AngularJS 1.0.6, advised by Igor from the Angular team, the issue is gone but now I've now got this one, which sounds more "normal" but I still can't figure out how to make it works.
Error: Injector already created, can not register a module!
I struggled with this error for a little while, but managed to come up with an sensible solution.
What I wanted to achieve is to successfully stub the Service and force a response, on controllers it was possible to use $httpBackend with a request stub or exception before initiating the controller.
In app.run() when you load the module it initialises the object and it's connected Services etc.
I managed to stub the Service using the following example.
describe('Testing App Run', function () {
beforeEach(module('plunker', function ($provide) {
return $provide.decorator('config', function () {
return {
load: function () {
return {};
}
};
});
}));
var $rootScope;
beforeEach(inject(function (_$rootScope_) {
return $rootScope = _$rootScope_;
}));
it("defines a value I previously could not test", function () {
return expect($rootScope.value).toEqual('testing');
});
});
I hope this helps your app.run() testing in the future.
I don't know if you are still looking for an answer to this question. But here is some information that might help.
$injector is a singleton for an application and not for a module. However, angular.injector will actually try to create a new injector for each module (I suppose you have a
beforeEach(module("app"));
at the beginning.
I had the same problem while using Angular, RequireJS, Karma and Jasmine and I figured out two ways to solve it. I created a provider for the injector function as a separate dependency in my tests. For example MyInjectorProvider which provides a singleton instance of $injector.
The other way was to move the following statements:
beforeEach(module("app"));
beforeEach(inject(function($injector){
...
})
inside the test suite description. So here is how it looked before:
define(['services/SignupFormValidator'], function(validator){
var validator;
beforeEach(module("app"));
beforeEach(inject(function($injector){
validator = $injector.get("SignupFormValidator");
})
describe("Signup Validation Tests", function(){
it("...", function(){...});
});
});
After applying the fix it looks like this:
define(['services/SignupFormValidator'], function(validator){
var validator;
describe("Signup Validation Tests", function(){
beforeEach(module("app"));
beforeEach(inject(function($injector){
validator = $injector.get("SignupFormValidator");
});
it("...", function(){...});
});
});
Both the solutions worked in my case.
You should mock every HTTP request with ngMock.$httpBackend. Also, here is a guide.
Update
You don't need the angular.mock.module thing, just need to inject your app module. Something like this:
var httpBackend;
beforeEach(module('app'));
beforeEach(inject(function($httpBackend) {
httpBackend = $httpBackend;
$httpBackend.expectGET('/config').respond(200, {'googleAnalyticsAccount': 'something'});
}));
In your tests, when you need the mocked http to answer, you will call httpBackend.flush(). This is why we have a reference to it, so you don't need to inject it in every single test you have.
Note you will need to load angular-mock.js in order to it work.

Categories

Resources