TypeError: Cannot read properties of undefined (reading 'length') Angular unit test - unit-testing

the error says its reading length meaning menuResponse is empty as the data is coming from a service and I have mocked the service as the service has multiple other dependable services and that is why I decided to mock the service.
ts file
getDemoProposedMenu(){
let menuResponse = service.menu.data;
for (var i = 0; i < menuResponse.length; i++) {
---code---
}
What I wanted to do was initialize the menuResponse on the spec file below but since menuResponse is initialized inside the method, it cannot find it. Is there an alternate way to do this?
spec.ts
beforeEach(() => {
fixture = TestBed.createComponent(InputGridNestedComponent);
component = fixture.componentInstance;
component.menuResponse = --copy paste the service data--
fixture.detectChanges();
})

Related

How to mock Fastify plugin

I am trying to write unit testing for fastify application which also has custom fastify plugin.
Is there a way we can mock fastify plugin? I tried mocking using Jest and Sinon without much success.
Giorgios link to the file is broken, the mocks folder is now absent from the master branch. I dug the commit history to something around the time of his answer and I found a commit with the folder still there. I leave it here for those who will come in the future!
This is what works for me
Setup your plugin according to Fastify docs https://www.fastify.io/docs/latest/Reference/Plugins/
// establishDbConnection.ts
import fp from 'fastify-plugin';
import {FastifyInstance, FastifyPluginAsync} from 'fastify';
import { initDbConnection } from './myDbImpl';
const establishDbConnection: FastifyPluginAsync = async (fastify: FastifyInstance, opts) => {
fastify.addHook('onReady', async () => {
await initDbConnection()
});
};
export default fp(establishDbConnection);
mock the plugin with jest, make sure you wrap the mock function in fp() so that Fastify recognizes it as a plugin.
// myTest.ts
import fp from 'fastify-plugin';
const mockPlugin = fp(async () => jest.fn());
jest.mock('../../../fastifyPlugin/establishDbConnection', (() => {
return mockPlugin;
}));
Your question is a bit generic but if you are using Jest it must be enough for mocking a fastify plugin. You can take a look in this repo and more specifically this file . This is a mock file of fastify and you add the registered plugins and in the specific example addCustomHealthCheck and then in your test files you can just call jest.mock('fastify').
You do not give a specific use case and there are lot of reasons you might want to mock a plugin. The nature of the plugin to be mocked is important to giving a good answer. Because I don't know that specific information I will show how to mock a plugin that creates a decorator that stores data that can be retrieved with fastify.decorator-name. This is a common use case for plugins that connect to databases or store other widely needed variables.
In the below case, the goal is to test a query function that queries a db; a plugin stores the connection information via a fastify decorator. So, in order to unit test the query we specifically need to mock the client data for the connection.
First create an instance of fastify. Next, set up a mock to return the desired fake response. Then, instead of registering the component with fastify (which you could also do), simply decorate the required variables directly with mock information.
Here is the function to be tested. We need to mock a plugin for a database which creates a fastify decorator called db. Specifically, in the below case the function to be tested uses db.client:
const fastify = require("fastify")({ //this is here to gather logs
logger: {
level: "debug",
file: "./logs/combined.log"
}
});
const HOURS_FROM_LOADDATE = "12";
const allDataQuery = `
SELECT *
FROM todo_items
WHERE a."LOAD_DATE" > current_date - interval $1 hour
`;
const queryAll = async (db) => {
return await sendQuery(db, allDataQuery, [HOURS_FROM_LOADDATE]);
};
//send query to db and receive data
const sendQuery = async (db, query, queryParams) => {
var res = {};
try {
const todo_items = await db.client.any(query, queryParams);
res = todo_items;
} catch (e) {
fastify.log.error(e);
}
return res;
};
module.exports = {
queryByAsv
};
Following is the test case. We will mock db.client from the db plugin:
const { queryAll } = require("../src/query");
const any = {
any: jest.fn(() => {
return "mock response";
})
};
describe("should return db query", () => {
beforeAll(async () => {
// set up fastify for test instance
fastify_test = require("fastify")({
logger: {
level: "debug",
file: "./logs/combined.log",
prettyPrint: true
}
});
});
test("test Query All", async () => {
// mock client
const clientPromise = {
client: any
};
//
fastify_test.decorate("db", clientPromise);
const qAll = await queryAll(fastify_test.db);
expect(qAll).toEqual("mock response");
});
});

Mock Unit tests for Linq statements in .Net core 2.2 always return null

I know lots of mocking questions are asked, but none worked with me.
I'm trying to write a unit test for a service I have. The service have the following line of code
var assignments = await _assignmentRepository.WhereAsync(as => as.DepartmentId == departmentId);
Here's the implementation of WhereAsync method:
public async Task<List<T>> WhereAsync(Expression<Func<T, bool>> expression)
{
return await _dbContext.Set<T>().Where(expression).ToListAsync();
}
Here's my mock test statement (listAssignments is a predefined variable):
_assignmentRepository.Setup(rep => rep.WhereAsync(as => It.IsAny<bool>())).ReturnsAsync(listAssignments);
I know that we can't Mock Where and FirstOrDefault methods, but isn't there a way to mock my web service WhereAsync method??
As Tseng mentioned in the comment above. We don't mock the DbContext, we mock the repository itself.
So I used the InMemoryDatabase testing. Added some data to my in-memory database which made my DbContext return the data I want.
var mapOptions = new DbContextOptionsBuilder<MapViewerContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var identityOptions = new DbContextOptionsBuilder<AppIdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var mapContext = new MapViewerContext(_configuration.Object, mapOptions);
var appIdentityContext = new AppIdentityDbContext(_configuration.Object, identityOptions);

How to test custom web component with jest?

I would like to test some custom web components and use jest.js as test runner (due to its support for ES6).
Chromium supports commands like
window.customElements.define('my-custom-element', MyCustomElementClass);
to register a custom web component.
However, window.customElements does not seem to be known in the context of jest tests.
As a work around I tried to use jest in combination with puppeteer and express to run the customElements part in Chromium.
However, I have difficulties to inject the custom element class TreezElement in the evaluated code:
treezElement.js:
class TreezElement extends HTMLElement {
connectedCallback () {
this.innerHTML = 'Hello, World!';
}
}
treezElement.test.js:
import TreezElement from '../../src/components/treezElement.js';
import puppeteer from 'puppeteer';
import express from 'express';
describe('Construction', ()=>{
let port = 3000;
let browser;
let page;
let element;
const width = 800;
const height = 800;
beforeAll(async () => {
const app = await express()
.use((req, res) => {
res.send(
`<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
</html>`
)
})
.listen(port);
browser = await puppeteer.launch({
headless: false,
slowMo: 80,
args: [`--window-size=${width},${height}`]
});
var pages = await browser.pages();
page = pages[0];
await page.setViewport({ width, height });
await page.goto('http://localhost:3000');
element = await page.evaluate(({TreezElement}) => {
console.log('TreezElement:')
console.log(TreezElement);
window.customElements.define('treez-element', TreezElement);
var element = document.create('treez-element');
document.body.appendChild(element);
return element;
}, {TreezElement});
});
it('TreezElement', ()=>{
});
afterAll(() => {
browser.close();
});
});
Maybe TreezElement is not serializable and therefore undefined is passed to the function.
If I try to import the custom element class TreezElement directly from within the evaluated code ...
element = await page.evaluate(() => {
import TreezElement from '../../src/components/treezElement.js';
console.log('TreezElement:')
console.log(TreezElement);
window.customElements.define('treez-element', TreezElement);
var element = document.create('treez-element');
document.body.appendChild(element);
return element;
});
... I get the error
'import' and 'export' may only appear at the top level
=> What is the recommended way to test custom web components with jest?
Some related stuff:
Web Component / HtmlElement : unit testing
https://itnext.io/testing-your-javascript-in-a-browser-with-jest-puppeteer-express-and-webpack-c998a37ef887
How to mock dependencies for ES6 unit tests?
Can't get test coverage with jest + puppeteer
https://jestjs.io/docs/en/puppeteer
JSDOM 16.2 includes basic support for custom elements and is available in Jest 26.5 and above. Here's a simple Jest test that shows it working:
customElements.define('test-component', class extends HTMLElement {
constructor() {
super();
const p = document.createElement('p')
p.textContent = 'It works!'
this.appendChild(p)
}
})
test('custom elements in JSDOM', () => {
document.body.innerHTML = `<h1>Custom element test</h1> <test-component></test-component>`
expect(document.body.innerHTML).toContain('It works!')
})
Output:
$ jest
PASS ./test.js
✓ custom elements in JSDOM (11 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.409 s
Ran all test suites.
Note not all features are supported yet, notably shadow DOM does not work.
I have created a DOM that supports server side rendering of web components. It also supports testing web components with Jest.
DOM:
https://www.npmjs.com/package/happy-dom
Jest environment:
https://www.npmjs.com/package/jest-environment-happy-dom
To install it
npm install jest-environment-happy-dom --save-dev
To use it:
Edit your package.json to include the Jest environment:
{
"scripts": {
"test": "jest --env=jest-environment-happy-dom"
}
}
use electron runner can include all node and chrome env,
use this to replace jsdom
https://github.com/facebook-atom/jest-electron-runner
Here is an ugly version that kind of works. Some further notes on this:
express.js is configured to work as file server. Otherwise there would be issues with mime type or cross origin checking for the imported ES6 modules.
The class TreezElement is not directly imported but using the work around of creating an extra script tag
There are issues with instance methods regarding the code coverage. It does not seem to be possible to directly call the constructor of TreezElement (inherits from HTMLElement, => illegal constructor). An instance of the element class can only be created with document.createElement(...) in puppeteer. Therefore, all the instance methods cannot be tested in Jest but only static methods. The instance methods and properties can be tested in puppeteer. However, the code coverage of jest does not consider the code coverage of puppeteer.
The created element of type TreezElement can be returned in form of an ElementHandle. Accessing the properties and methods of the element instance is quite cumbersome (see example below). As an alternative to the handle approach, the page.$eval method can be applied:
var id = await page.$eval('#custom-element', element=> element.id);
index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="root"></div>
</body>
</html>
treezElement.test.js
import TreezElement from '../../src/components/treezElement.js';
import puppeteer from 'puppeteer';
import express from 'express';
describe('Construction', ()=>{
let port = 4444;
const index = Math.max(process.argv.indexOf('--port'), process.argv.indexOf('-p'))
if (index !== -1) {
port = +process.argv[index + 1] || port;
}
var elementHandle;
beforeAll(async () => {
const fileServer = await express()
.use(express.static('.'))
.listen(port);
var browser = await puppeteer.launch({
headless: false,
slowMo: 80,
userDataDir: '.chrome',
args: ['--auto-open-devtools-for-tabs']
});
var pages = await browser.pages();
var page = pages[0];
await page.goto('http://localhost:'+port + '/test/index.html');
await page.evaluate(() => {
var script = document.createElement('script');
script.type='module';
script.innerHTML="import TreezElement from '../src/components/treezElement.js';\n" +
"window.customElements.define('treez-element', TreezElement);\n" +
"var element = document.createElement('treez-element');\n" +
"element.id='custom-element';\n" +
"document.body.appendChild(element);";
document.head.appendChild(script);
});
elementHandle = await page.evaluateHandle(() => {
return document.getElementById('custom-element');
});
});
it('id', async ()=>{
var idHandle = await elementHandle.getProperty('id');
var id = await idHandle.jsonValue();
expect(id).toBe('custom-element');
});
afterAll(() => {
browser.close();
});
});
Another (limited) approach is to use Object.create as a work around to create an instance of the custom web component, without using window.customElements.define and document.createElement(..) :
import TreezElement from '../../src/components/treezElement.js';
var customElement = Object.create(TreezElement.prototype);
This way the instance methods can be tested directly in jest and the tests are included in the code coverage, too. (Also coverage issues of my other answer regarding puppeteer.)
A major disadvantage is that only the methods can be accessed, not the properties. If I try to use customElement.customProperty I get : TypeError: Illegal invocation.
This is caused by the check !module.exports.is(this) in Element.js:
Element.prototype.getAttribute = function getAttribute(qualifiedName) {
if (!this || !module.exports.is(this)) {
throw new TypeError("Illegal invocation");
}
...
Element.prototype.setAttribute = function setAttribute(qualifiedName,value){
if (!this || !module.exports.is(this)) {
throw new TypeError("Illegal invocation");
}
Another disadvantage of Object.create is that the constructor code is not called and not included in the test coverage.
If the command window.customElements.define(..) is directly included in a class file that we would like to import (e.g. treezElement.js) ... the customElements property needs to be mocked before including the import:
customElementsMock.js:
export default class CustomElementsMock{} //dummy export
//following command mocks the customElements feature to be able
//to import custom elements in jest tests
window.customElements = {
define: (elementName, elementClass)=>{
console.log('Mocked customElements.define(..) for custom element "' + elementName + '"');
}
};
usage in treezElement.test.js:
import CustomElementsMock from '../customElementsMock.js';
import TreezElement from '../../src/components/treezElement.js';
var customElement = Object.create(TreezElement.prototype);
//...
(I also tried to put the mock code directly at the beginning of treezElement.test.js but all the imports are executed prior to the script doing the import. That is why I had to put the mocking code in an extra file.)

Angular2 Component Unit Testing with Service Dependency

So I have this PromptComponent and the content will be updated by a service data.
Now I am trying to do the unit test this component which has this service dependency.
Here is the Component:
export class PromptComponent {
#Input() module: any;
#select() selectedPrompt;
constructor(private moduleService: ModuleService){}
}
Here is the service:
getQuote(): Promise<string> {
return new Promise(resolve => {
setTimeout( () => resolve(this.nextQuote()), 500 );
});
}
private nextQuote() {
if (this.next === quotes.length) { this.next = 0; }
return quotes[ this.next++ ];
}
Here is the unit test code:
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
PromptComponent,
],
providers: [ ModuleService]
});
fixture = TestBed.createComponent(PromptComponent);
component = fixture.componentInstance;
const moduleService = fixture.debugElement.injector.get(ModuleService);
spy = spyOn(moduleService, 'getQuote').and.returnValue(Promise.resolve(testService));
I referred to the official demo on the angular documentation
https://angular.io/resources/live-examples/testing/ts/eplnkr.html, as inside the TwainComponent.spec.
Something really weird is that as expected, inside the official demo, the TwainComponent test passed smoothly. But I, on the other hand, got this strange injectionError. If I remove the parameter inside the constructor, I am able to create the fixture instance but get injection error when it runs thorught the injector.get() part. But as soon as I put the private moduleService:ModuleService back inside the component constructor, I will get fixture undefined and also with the injection error.
So that means I cannot even execute my code to the actual service injection part (fixture.debugElement.injector.get(ModuleService)), I already got the error.
Does anybody have the same issue?
My code is almost identical to the demo code on Plunker. Have no clue how to move forward....

Unit test Angularjs file upload controller method

i'm trying to unit test a controllers method in Angularjs that is responsible for file upload:
$scope.uploadFile = function() {
var fd = new FormData();
for (var i in $scope.files) {
fd.append("uploadedFile", $scope.files[i]);
}
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", $scope.uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.open("POST", "/fileupload");
xhr.send(fd);
}
i tried to mock the xhr object like the following :
it("using $window ", inject(function($window) {
$window.XMLHttpRequest= angular.noop;
addEventListenerSpy = jasmine.createSpy("addEventListener");
openSpy = jasmine.createSpy("open");
sendSpy = jasmine.createSpy("send");
xhrObj = {
upload:
{
addEventListener: addEventListenerSpy
},
addEventListener: addEventListenerSpy,
open: openSpy,
send: sendSpy
};
spyOn($window, "XMLHttpRequest").andReturn(xhrObj);
}));
when i run karma test config file i have the following error :
TypeError: Attempted to assign to readonly property.
at workFn (/home/dre/trunk/app/bower_components/angular-mocks/angular-mocks.js:2107)
can anyone help me i'm new in unit testing with jasmine and karma
You should be using Angular dependency injection which greatly helps tests. You should also use Angular's $http or $resource service to perform XHR requests.
Having said that, I created a fiddle with your test.
Test implementation are missing in your question, e.g. controller creation.
I hope the full example gives you a clue as to the problem in your code.
it("using $window ", function () {
xhrObj = jasmine.createSpyObj('xhrObj',
['addEventListener', 'open', 'send']);
spyOn(window, "XMLHttpRequest").andReturn(xhrObj);
scope.uploadFile()
expect(xhrObj.addEventListener).toHaveBeenCalled();
expect(xhrObj.addEventListener.calls.length).toBe(2);
});
You can find the full example here.
But I urge you to use $http/$resource instead.