Angular2 unit testing : testing a component's constructor - unit-testing

All is in the title : how can one test what is done in the component's constructor ?
For your information, I am using a service that requires a setting, and I would like to see if the 2 methods that I call in the constructor are called correctly.
My component's constructor :
constructor(
public router: Router,
private profilService: ProfileService,
private dragula: DragulaService,
private alerter: AlertService
) {
dragula.drag.subscribe((value) => {
this.onDrag(value);
});
dragula.dragend.subscribe((value) => {
this.onDragend(value);
});
}

I would inject a fake service using the DI system, which would mean writing the tests something like this:
describe('your component', () => {
let fixture: ComponentFixture<YourComponent>;
let fakeService;
let dragSubject = new ReplaySubject(1);
...
beforeEach(async(() => {
fakeService = {
drag: dragSubject.asObservable(),
...
};
TestBed.configureTestingModule({
declarations: [YourComponent, ...],
providers: [
{ provide: DragulaService, useValue: fakeService },
...
],
});
}));
beforeEach(() => {
fixture = TestBed.createComponent(YourComponent);
fixture.detectChanges();
});
it('should do something when a drag event occurs', () => {
dragSubject.next({ ... });
fixture.detectChanges();
...
});
});
This allows you to trigger "drag events" whenever you like by calling .next on the subject, which causes subscribers to the fields on the fake service to get called. You can then make assertions on the outcomes you expect from that.
Note that you do not need to call constructor yourself; this method is invoked when the DI system instantiates your component, i.e. when TestBed.createComponent is called.
I would recommend that you don't spy on the component methods (e.g. this.onDrag) and just make sure that they get called, but rather test that whatever those methods should do as a result happens; this makes the tests more robust to changes in the specific implementation (I wrote a bit about this on my blog: http://blog.jonrshar.pe/2017/Apr/16/async-angular-tests.html).

The simple way to test anything inside constructor function is to create component instance and then test it.
it('should call initializer function in constructor', () => {
TestBed.createComponent(HomeComponent); // this is the trigger of constructor method
expect(sideNavService.initialize).toHaveBeenCalled(); // sample jasmine spy based test case
});
One thing to note that, if you want to distinguish between constructor and ngOnInit, then don't call fixture.detectChanges() inside beforeEach(). instead call manually whenever you need.

Since OP states "I would like to see if the 2 methods that I call in the constructor are called correctly." I have a better approach.
Write a unit test. You don't need to use the test bed for this. It will slow down your tests a lot. Instantiate your mocks manually. Set your spies on the methods you're interested in and then call the component constructor manually with the stubs you've instantiated and set spies on. Then test if spied methods have been called correctly.
The key is to extend your stubs from the original service classes. jasmine.createSpyObj helps for mocking angular classes like Router.

Related

element is not rendering properly with *ngIf async pipe in unit test spec

What I'm trying to do is get the reference of a button in order to simulate a click event, my main problem is that the button is not rendering cause the component is not detecting the change of a list that should be populated by that time.
Here is my unit test code :
it('should add more cases when load more cases button is clicked', () => {
spyOn(component, 'loadMoreCases');
component.cases$.subscribe((cases) => {
fixture.detectChanges();
let buttons = fixture.debugElement.nativeElement.querySelectorAll('[nz-button]') as HTMLButtonElement[];
buttons[1].click();
expect(component.loadMoreCases).toBeCalled;
});
component.ngAfterViewInit();
fixture.detectChanges();
});
here is the *ngIf part in the HTML:
enter image description here
here is the service that is called after view init in the main component:
ngAfterViewInit(): void {
this.caseService
.getPaginatedCases(1, 15)
.pipe(take(1))
.subscribe((cases) => {
this.cases$.next(cases.list);
this.amountOfCases = cases.total;
});
}
I just want to know how to tell angular that the cases have some mock data already and inform the component to rerender in order to be able to access the button that it's in the *ngIf, I had been trying with everything, fakeAsync() using the tick function, async using done function, but nothing had worked.
How are you providing the data? Mocking the service and then returning them should be enough for it to work:
const getPaginatedCases = spyOn(caseService, "getPaginatedCasees");
getPaginatedCases.and.returnValue(of(mockCases));
Of course, it's important that this is run before ngOnInit() (you have the initializer in ngAfterViewInit(), but I think it belongs in init – doesn't matter for the sake of this answer anyway) is called, so fixture.detectChanges() can only be called after this has run. Don't forget that you might have a fixture.detectChanges() in the beforeEach() that is messing you up.

Checking middleware is called from http call

How would one test that a piece of custom middleware is actually called from a standard HTTP event?
ie. The middleware is called from:
MyController.js
router.get('/some/endpoint', [myMiddleware()], (req, res, next) => {
// Code to do whatever here
});
The middleware itself can be defined as:
MyMiddleware.js
module.exports = () => {
// Middleware code in here
}
My quest is to check that the middleware is called once from my unit test, but I cannot find documentation around this.
MyTest.test.js
it('Should return whatever from GET call', () => {
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {res.body.should.deep.equal(bodyValue)});
// How would I place code in here to check that MyMiddleware is called?
// ie. sinon.assert.calledOnce(MyMiddleware)
});
I have thought about using Sinon's spy, but I can't think of how to hook into the middleware... My attempt was this:
const mwSpy = sinon.spy(require('path to middleware file'));
sinon.assert(calledOnce(mwSpy));
The usual way of going about this is splitting this into two tests, an integration test and a unit test.
Will the middleware I specified in the router.get call end up being called when someone hits this endpoint?
Does my middleware do the right thing?
The first part is basically testing that the Express API is doing what the documentation says. That's not what unit tests are for (this was tagged unit-testing), but since you are already using HTTP requests to test the endpoint, I guess that's not what you are after anyway: you are basically creating verification tests for your system.
You could still test the Express routing without HTTP, though, as I detail in the answer to this question, concerning how to test the router programmatically (faster tests, no http), but less just stick to what you have.
So the basic question is: "My quest is to check that the middleware is called once from my unit test". You don't seem to concern yourself with whether the middleware is doing the right thing or not, just that it's called, which calls for the question on whether we should test the middleware or the layer using the middleware.
In both cases, you need to find a way of injecting a test spy. Either you write a small utility method that will inject that spy: function setMiddleware(module){ middleware = module; } or you use some tooling like proxyquire. See this tutorial on Sinon's homepage for background.
I would just do this (in the test code):
it('Should return whatever from GET call', () => {
var middlewareFake = sinon.fake();
// I am assuming it's the actual app object you are referencing below in the request(app) line
var app = proxyquire('../app/index.js', { './my-middleware': middlewareFake });
//
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {
res.body.should.deep.equal(bodyValue)
expect(middlewareFake).was.called;
});
});

spy on mocked object accessor (get and set)

Mocking out dependencies while testing angular components. ComponentA injects ServiceA, I want to mock serviceA in my test
class ServiceA {
private _prop = 1;
get prop (): number {
return this._prop;
}
set prop(p: number) {
this._prop;
}
}
class ComponentA {
private injectedService: ServiceA;
constructor(private injectedService: ServiceA) {
this.injectedService = injectedService;
}
method () {
this.injectedService.prop = this.injectedService.prop + 1;
if (this.injectedService.prop === 5) {
this.injectedService.prop = 1;
}
}
}
With the above program structure it is important to have a serviceA mock that has accessor(getter and setter for injectedService.prop) implementation, since a part of the program is dependent on the value of injectedService.prop that was prior set.
Options that I have tried:
ts-mockito: ts-mockito mocks does not have access type set. With this limitation ts setter won't be recognised. A workaround would be to define actual methods to 'set' and 'get' and call these methods. setter would still need a more workaround as below:
// default get stub
when(mock.getProp()).thenReturn();
// setter redefines get stub
when(mock.setProp(anything())).thenCall((p) => {
when(mock.getProp()).thenReturn(a);
});
typemoq:
static mock calls constructor, big flaw when target mock class as other dependency injection(s)
dynamic mock conditions like (this.injectedService.prop === 5) will never be truthy beacuse this.injectedService.prop (i.e mock.object.prop) points to a function. On this I feel there could be a way that I'm yet to figure out, I would appreciate any help
(Personal preference) I prefer jasmine spyOn to typemoq verify for assertions on my mocks. This could be ignored if not possible.
Things I don't want to do
I do not want to modify classes to be mocked except to add standard typescript accessors
I do not want to create an Interface/Class off classes to be mocked just for the purpose of creating a mock for test
Although I'm open to compromise on above if there is a good justification or standard
I would also appreciate if anybody could direct me to code base(s) that has proper test and follows standard if any.
Note: I'm testing an angular app with karma + jasmine ... but all these doesn't count as I am merely creating class from constructor, no Testbed just simple typescript class unit-test.

Mock Action functions in Jest test

How would one go about calling a function from an imported action module? Here is my component
class Task extends Component {
handleClick(e) {
e.preventDefault();
Actions.returnTask(this.props.id);
}
render() {...}
}
and tests that look like this:
jest.dontMock('./Task');
jest.dontMock('./Actions');
describe('Tasks', function() {
let renderedComponent;
let mockReturnTask;
let TaskActions;
beforeEach(function() {
renderedComponent = TestUtils.renderIntoDocument(
<Task task={testTask} key={1} />
);
Actions = require('./Actions');
mockReturnTask = jest.genMockFn();
Actions.returnTask = mockReturnTask;
});
it('should call a returnTask action on Actions', function() {
renderedComponent.handleClick(event);
expect(mockReturnTask).toBeCalled();
});
});
When running my test it tells me that the function was not called. If I do expect(Actions.returnTask).toBeCalled(); I get an error message that toBeCalledWith() should be used on a mock function error. How do I mock a function on the external Actions and check that it is called in my tests? I feel like my method above should be working.
Your example doesn't include the require of Task but I'm pretty sure of what happened here. Jest users must be aware of what require they do, and when.
Chronologically:
jest is set to never mock Actions and Task
Task component is required, jest requires it for real
Actions is required because it is required in the Task component. jest requires it for real.
Task component is instantiated
Actions.returnTask is monkey-patched but it's not the function the component is bind with, it's a new function who exist only in the test.
To make your component using a jest mocked function, you have to require and monkey-patch Actions before requiring Tasks
Be aware that you don't have to unmocked Actions if you mock a function of Actions right after. It's exactly what the jest auto-mocking killing feature is made for : remove jest.dontMock('./Actions'); and all references to your Actions module. The test should be working.

Injecting mocks into Browserify for testing

I know, Browserify isn't really a DI framework, but is it possible to "inject", or somehow fake injecting, mock data into an application during unit testing?
For example, to test the function:
var MyModel = require('./models/My.js');
function doSomething() {
// do something with model.
}
with a mock My.js, like
describe('Do Something', function() {
beforeEach(function() {
// replace './models/My.js' with a Mock implementation.
});
it('with model', function() {
// ... test
});
})
what goes in the beforeEach function?
There are a few tools for mocking require calls in browserify.
https://github.com/thlorenz/proxyquireify
https://github.com/i-like-robots/rewireify
https://github.com/thlorenz/browserify-swap
https://github.com/Colingo/mock
https://github.com/mfncooper/mockery
I haven't personally used these. Also, mockery wasn't written with Browserify in mind so mockery might not even work. The others were written for Browserify though so they should work with little effort. :) Proxyquireify and Rewireify seem to be the only 2 active within the last year though.