AngularJS - basic testing with injection - unit-testing

So I'm new to the whole testing thing (I've been one of those people who has said 'I should write unit tests...' but never ended up ever doing it :-p).
I'm now writing unit tests for this project.  I'm using testacular + Jasmine, with browserify to compile things.  I was having no problems until I started trying to do a lot AngularJS injection-stuff.
Right now I'm simply trying to do a test of ng-model to get my head around all of it.
I have a testacular.conf file which includes everything necessary:
files = [
'../lib/jquery.js',
'../lib/angular.js',
'./lib/jasmine.js',
'./lib/angular-mocks.js',
JASMINE_ADAPTER,
'./tests.js' //compiled by browserify
];
I have my controller defined (MainCtrl.coffee)
MainCtrl = ($scope, $rootScope) ->
$scope.hello = 'initial'
module.exports = (angularModule) ->
angularModule.controller 'MainCtrl', ['$scope', '$rootScope', MainCtrl]
return MainCtrl
And I have my test itself: (_MainCtrlTest.coffee, in same directory as MainCtrl.coffee)
testModule = angular.module 'MainCtrlTest', []
MainCtrl = require('./MainCtrl')(testModule)
describe 'MainCtrlTest', ->
scope = null
elm = null
ctrl = null
beforeEach inject ($rootScope, $compile, $controller) ->
scope = $rootScope.$new()
ctrl = $controller MainCtrl, $scope: scope
elm = $compile('<input ng-model="hello"/>')(scope)
describe 'value $scope.hello', ->
it 'should initially equal input value', ->
expect(elm.val()).toBe scope.hello
it 'should change when input value changes', ->
scope.$apply -> elm.val('changedValue')
expect(scope.hello).toBe elm.val()
The test fails immediately, with the input's elm.val() returning blank, and scope.hello returning the intended value ('initial', set in MainCtrl.coffee)
What am I doing wrong here?

To get this working, you need to do scope.$apply():
it 'should initially equal input value', ->
scope.$apply()
expect(elm.val()).toBe scope.hello
Don't test the framework, test your code
Your test is trying to test whether Angular's binding, and ng-model works. You should rather trust the framework and test your code instead.
Your code is:
the controller (setting initial scope.hello value)
html templates (and all the binding, directives in there)
You can test the first one very easily, without even touching any DOM. That's the beauty of AngularJS - strong separation of view/logic.
In this controller, there is almost nothing to test, but the initial value:
it 'should init hello', ->
expect(scope.hello).toBe 'initial'
To test the second one (template + binding), you want to do an e2e test. You basically want to test, whether the template doesn't contain any typos in binding etc... So you wanna test the real template. If you inline a different html during the test, you are testing nothing but AngularJS.

Related

How to skip mocked image and get real image attributes with Jest?

I am writing unit tests and I want to test an img atrributes. However, when I make an assertion, I get mocked img attributes which is under __mocks__>fileMock.js. Because I mock files, I only get mocked file atrributes. How can I skip mocked files in my tests?
describe('Buttons', () => {
test('Clear filters work after filtering tools, workflows and using searchbar', async () => {
render(<HeaderBar {...defaults} />);
const clearFilterButton = screen.getByRole('button', { name: 'Clear Filters' });
const toolsButton = screen.getByRole('button', { name: 'TOOLS' });
const selectedTools = await within(toolsButton).findByRole('img');
expect(selectedTools).toHaveAttribute('src', 'images/tools-selected.png');
});
And test result is :
Buttons › Clear filters work after filtering tools, workflows and using searchbar
expect(element).toHaveAttribute("src", "images/tools-selected.png") // element.getAttribute("src") === "images/tools-selected.png"
Expected the element to have attribute:
src="images/tools-selected.png"
Received:
src="test-file-stub"
39 | const toolsButton = screen.getByRole('button', { name: 'TOOLS' });
40 | const selectedTools = await within(toolsButton).findByRole('img');
> 41 | expect(selectedTools).toHaveAttribute('src', 'images/tools-selected.png');
I need to test real image, and skip mocking that img in my test.
It sounds like you have a manual user module mock defined in a __mock__ folder next to your actual code.
In that case the mock is used in any test file where you call jest.mock('moduleName') unless automock is set to true in which case the mock will always be used.
If you are explicitly mocking the file using jest.mock('moduleName') then simply remove that from the test file where you want to use the actual code instead of the mock.
If you have automock set to true in your Jest config then you can tell Jest to use the original code file in a given test by using jest.unmock('moduleName').

Stencil, Leaflet, unit testing component, gives TypeError: Cannot read property 'deviceXDPI' of undefined

So we are developing a Stenciljs component which wraps leaflet map and adds some additional functionality.
Now obviously we don't want or need to test Leaflet, but instead, just the parts in our wrapper components.
So, using the test examples, we create our tests,
import { LeMap } from "./le-map";
describe("Map component tests", () => {
it("Should build the map component", async () => {
const map = new LeMap();
expect(map).not.toBeNull();
});
});
try and load the components and test the public functions, but we get
TypeError: Cannot read property 'deviceXDPI' of undefined
> 1 | import {Component, Element, Listen, Method, Prop, Watch} from
'#stencil/core';
> 2 | import L from 'leaflet';
| ^
3 |
4 | #Component({
5 | shadow: false,
We believe this message is because the test is trying to render leaflet, and because it's not a true browser, it can't detect a view so throwing this error, so we've tried to mock leaflet in the tests, but still get the same problem.
We're tried to mock the leaflet module by using jest mocking
jest.genMockFromModule('leaflet');
but this made no diffrence
Only idea I've had is to separate the logic from the components, but that feels wrong, as we'd just be doing this for purpose of testing.
Versions in use are: leaflet: 1.3.4, #stencil: 0.15.2, jest: 23.4.2
Any other suggestions?
Further investigation with, thanks to #skyboyer 's suggestions, leads me to this line of the leaflet core browser.js file
leads me to this line of the leaflet core browser.js file
export var retina = (window.devicePixelRatio || (window.screen.deviceXDPI/window.screen.logicalXDPI)) > 1;
But I'm unable to mock the screen property of window as I get the following error
[ts] Cannot assign to 'screen' because it is a constant or a read-only property,
so I try the following.
const screen = {
deviceXDPI:0,
logicalXDPI:0
}
Object.defineProperty(window, 'screen', screen);
Object.defineProperty(window, 'devicePixelRatio', 0);
Same error, completely ignores this, so I try over riding the export.
jest.spyOn(L.Browser,'retina').mockImplementation(() => false);
No joy either, so tried
L.Browser.retina = jest.fn(() => false);
but get it tells me it's a constant and can't be changed (yet the implication stats var so ¯_(ツ)_/¯ )
Anything else I can try?
Further update,
I've managed to mock the window, but this sadly doesn't solve it.
const screenMock = {
deviceXDPI: 0,
logicalXDPI: 0
}
const windowMock = {
value: {
'devicePixelRatio': 0,
'screen': screenMock
}
}
Object.defineProperty(global, 'window', windowMock);
If I console log this, I get the right properties but as soon as I test the instantiation of the component it fails with
TypeError: Cannot read property 'deviceXDPI' of undefined
Reading around it seems Leaflet doesn't check for a DOM and just tries to render anyway, I can't see anyway around this, I saw a leaflet-headless package, but I don't know how I could swap them out just for testing.
Think I will need to look at another strategy for testing, probably protractor.
Found a solution, not fully tested yet, but the tests pass.
I did it by creating a
__mocks__
directory at the same level as the node_modules directory.
created a file called leaflet.js in it. It's a simple file it just contains.
'use strict';
const leaflet = jest.fn();
module.exports = leaflet;
then in my test file (le-map.spec.ts) I just added
jest.mock('leaflet')
before the imports
and now my test passes.
I tried doing this in the test itself but that just gave me the same error, it must be something in the loading sequence which means it has to be manually mocked beforehand.
Hope this helps others, it's been driving me mad for weeks.

Unit testing a directive that defines a controller in AngularJS

I'm trying to test a directive using Karma and Jasmine that does a couple of things. First being that it uses a templateUrl and second that it defines a controller. This may not be the correct terminology, but it creates a controller in its declaration. The Angular application is set up so that each unit is contained within its own module. For example, all directives are included within module app.directive, all controllers are contained within app.controller, and all services are contained within app.service etc.
To complicate things further, the controller being defined within this directive has a single dependency and it contains a function that makes an $http request to set a value on the $scope. I know that I can mock this dependency using $httpBackend mock to simulate the $http call and return the proper object to the call of this function. I've done this numerous times on the other unit tests that I've created, and have a pretty good grasp on this concept.
The code below is written in CoffeeScript.
Here is my directive:
angular.module('app.directive')
.directive 'exampleDirective', [() ->
restrict: 'A'
templateUrl: 'partials/view.html'
scope: true
controller: ['$scope', 'Service', ($scope, Service) ->
$scope.model = {}
$scope.model.value_one = 1
# Call the dependency
Service.getValue()
.success (data) ->
$scope.model.value_two = data
.error ->
$scope.model.value_two = 0
]
]
Here is the dependency service:
angular.module("app.service")
.factory 'Service', ['$http', ($http) ->
getValue: () ->
options.method = "GET"
options.url = "example/fetch"
$http _.defaults(options)
Here is the view:
<div>
{{model.value_one}} {{model.value_two}}
</div>
I've simplified this quite a bit, as my goal is only to understand how to wire this up, I can take it from there. The reason I'm structuring it this way is because I did not initially create this. I'm working on writing tests for an existing project and I don't have the ability to configure it any other way. I've made an attempt to write the test, but cannot get it to do what i want.
I want to test to see if the values are being bound to the view, and if possible to also test to see if the controller is creating the values properly.
Here is what I've got:
'use strict'
describe "the exampleDirective Directive", ->
beforeEach module("app.directive")
beforeEach module("app/partials/view.html")
ServiceMock = {
getValue : () ->
options.method = "GET"
options.url = "example/fetch"
$http _.defaults(options)
}
#use the mock instead of the service
beforeEach module ($provide) ->
$provide.value "Service", ServiceMock
return
$httpBackend = null
scope = null
elem = null
beforeEach inject ($compile, $rootScope, $injector) ->
# get httpBackend object
$httpBackend = $injector.get("$httpBackend")
$httpBackend.whenGET("example/fetch").respond(200, "it works")
#set up the scope
scope = $rootScope
#create and compile directive
elem = angular.element('<example-directive></example-directive>')
$compile(elem)(scope)
scope.$digest()
I don't know how close I am, or if this is even correct. I want to be able to assert that the values are bound to the view correctly. I've used Vojtajina's example to set up html2js in my karma.js file to allow me to grab the views. I've done a lot of research to find the answer, but I need some help. Hopefully a programmer wiser than I can point me in the right direction. Thank you.
Create the element in karma, then use the .controller() function with the name of your directive to grab the controller. For your example, replace the last couple of lines with these:
elem = angular.element('<div example-directive></div>');
$compile(elem)($rootScope);
var controller = elem.controller('exampleDirective');
Note, that given how you defined your directive, it should be by attribute, and not as an element. I'm also not 100% sure, but I don't think you need the scope.$digest; usually I just put anything that needs to be applied into a scope.$apply(function() {}) block.

Testing Ember (v1.0.0-rc.3) nested controllers using Mocha and Chai

I am trying to write test cases for controllers of an Ember (v1.0.0-rc.3) Application using Mocha and Chai. One of my controller is making use of another controller as follows
App.ABCController = Em.ObjectController.extend({
needs: ['application'],
welcomeMSG: function () {
return 'Hi, ' + this.get('controllers.application.name');
}.property(),
...
});
I wrote testCase as below:
describe 'ABCController', ->
expect = chai.expect
App = require '../support/setup'
abcController = null
before ->
App.reset()
ApplicationController = require 'controllers/application_controller'
ABCController = require 'controllers/abc_controller'
applicationController = ApplicationController.create()
abcController = ABCController.create()
describe '#welcomeMSG', ->
it 'should return Hi, \'user\'.', ->
msg = abcController.get('welcomeMSG')
expect(msg).to.be.equal('Hi, '+ applicationController.get('name'))
support/setup file is as follows
Em.testing = true
App = null
Em.run ->
App = Em.Application.create()
module.exports = App
Now whenever i try to run testcase i face error
"Before all" hook:
TypeError: Cannot call method 'has' of null
at verifyDependencies (http://localhost:3333/test/scripts/ember.js:27124:20)
at Ember.ControllerMixin.reopen.init (http://localhost:3333/test/scripts/ember.js:27141:9)
at superWrapper [as init] (http://localhost:3333/test/scripts/ember.js:1044:16)
at new Class (http://localhost:3333/test/scripts/ember.js:10632:15)
at Function.Mixin.create.create (http://localhost:3333/test/scripts/ember.js:10930:12)
at Function.Ember.ObjectProxy.reopenClass.create (http://localhost:3333/test/scripts/ember.js:11756:24)
at Function.superWrapper (http://localhost:3333/test/scripts/ember.js:1044:16)
at Context.eval (test/controllers/abc_controller_test.coffee:14:47)
at Hook.Runnable.run (test/vendor/scripts/mocha-1.8.2.js:4048:32)
at next (test/vendor/scripts/mocha-1.8.2.js:4298:10)
Please help me to resolve this issue. I will appreciate if someone provide me few links where i can study for latest ember.js application testing with mocha and chai.
Reading trough the docs I guess your problem is that your expect fails due to this to.be.equal. Try changing the assertion chain to this:
expect(msg).to.equal('Hi, '+ applicationController.get('name'))
Update
After reading your comment I guess the problem in your case is that when you do Ember.testing = true is equivalent to the before needed App.deferReadiness(), so it' obviously necessary to 'initialize' your App before using it, this is done with App.initialize() inside your global before hook. And lastly you call App.reset() inside your beforeEach hook's.
Please check also this blog post for more info on the update that introduced this change.
Hope it helps

Grails 1.3.7 to Grails 2.2 upgrade, Unit tests now fail with missing GORM methods

I am in the middle of upgrading an app from Grails 1.3.7 to 2.2
So far, its been relatively painless and straight forward.
Until we started running the unit tests.
Under 1.3.7, all the tests passed.
Under 2.2, about half are now failing. The tests haven't changed, they are still the old style mockDomain...
What is most concerning to me is that basic gorm features are missing on some of the domain classes.
Things like .list and .get
Failure: testList_NoMaxSpecified_10Shown(com.litle.bldvwr.StreamControllerTests)
| groovy.lang.MissingMethodException: No signature of method: >com.litle.bldvwr.Stream.list() is applicable for argument types: () values: []
Possible solutions: list(), list(), list(), list(java.lang.Object), list(java.util.Map), >list(java.lang.Object)
and
Failure: >testAddFailureOutputToHappyPathWithIntegrationFailure(com.litle.bldvwr.LogParserServiceTests)
| groovy.lang.MissingMethodException: No signature of method: >com.litle.bldvwr.Result.get() is applicable for argument types: () values: []
Possible solutions: get(java.io.Serializable), get(java.lang.Object), >get(java.io.Serializable), getId(), grep(), grep(java.lang.Object)
The general pattern of for this type of failure is:
mockDomain(Phase, [new Phase(id:1, name: 'xxx')])
mockDomain(Result, [new Result(id:1, phase: Phase.get(1), failureOutput:"")])
logParserService.addFailureOutputTo(Result.get(1))
And it is that last get that is causing the no signature error.
While we intend to start using the new Unit Test functionality, I was hoping to avoid having to rewrite the 500+ current tests.
Thoughts, ideas?
-Clark
Using the new #Mock() annotation in your test for the domain objects will inject all the expected mock GORM methods, and you can even just save() your domain objects instead of providing the list in mockDomain() call.
#Mock([Result, Nightly])
class MyTests {
void testSomething() {
def night = new Nightly( name:'nightly1')
night.id=1
night.save(validate: false)
assert Nightly.get(1).name == 'nightly1'
assert Result.count() == 0
new Result(status: Constants.SUCCESS, type: Constants.INTEGRATION,
nightly: Nightly.get(1)).save(validate: false)
assert Result.count() == 1
assert Result.findByStatus(Constants.SUCCESS) != null // yay dynamic finders!
}
}
http://grails.org/doc/latest/guide/testing.html#unitTestingDomains
You'll have to update all your tests to the new ways, but it's much nicer that the old 1.3 ways.
So here is what we found.
With 1.3, you could do:
{
mockDomain(Nightly, [new Nightly(id: 7)])
mockDomain(Result, [
new Result(status: Constants.SUCCESS,
type: Constants.INTEGRATION, nightly: Nightly.get(7))
])
service.callSomething(results, Nightly.get(7))
assert result==Nightly.get(7).property
And it would work just fine. You whoul have a Mock domain object with and id of 7, and the get would work just fine.
Since then, something changed, and you can no longer set the id has part of the create.
What you need to do is this:
night = new Nightly( name:'nightly1')
night.id=1
mockDomain(Nightly, [night])
mockDomain(Result, [
new Result(status: Constants.SUCCESS, type: Constants.INTEGRATION, nightly: Nightly.get(1))
])
and that mostly sets up the mocks correctly.
The issue we ran into next was that outside of the mockDomain call, Nightly.get() would not work.
So now we need to save the "mocked" domains in local variables in order to do post action comparison and checks.
Not a completely horrible solution, but less elegant than we were hoping for.