Cypress Component Testing Angular Components spying on private service - angular-test

I am trying to use Cypress's new Component Testing for Angular.
Given the following test
beforeEach(() => {
mockService.ageRangeName = '0-5';
cy.viewport(1920, 1080);
cy.mount(CategoryBuilderComponent, {
componentProperties: {},
imports:[],
providers: [{ provide: CategoryBuilderService, useValue: mockService }],
});
it('should test click of cancel', () => {
cy.get('[data-test="cbc-cancel"]').click()
//has mockService.change.next been called
});
i want to determine if clicking the cancel button has called my private dependency injected service rxjs BehaviorSubject Property with a value of true.
The complete code sample can be found at https://github.com/hokrc01/cypress-component-testing-question-was-subject-launched

Related

Testing Angular 14 Standalone Components with Spectator

Our test runner is Jest.
Our component is marked as standalone: true,
If try to set up spectator like this:
describe('OurComponent', () => {
let spectator: Spectator<OurComponent>;
const fakeActivatedRoute: ActivatedRoute = {
snapshot: {data: {}},
} as ActivatedRoute;
const componentFactory: SpectatorFactory<OurComponent> = createComponentFactory({
component: OurComponent,
imports: [
// some imports
],
providers: [
// some providers
],
detectChanges: false,
shallow: true,
});
beforeEach(async () => {
spectator = componentFactory();
});
it('should be created', () => {
expect(spectator).toBeDefined();
});
});
Then we run into the following error:
"Error: Unexpected "OurComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call, "OurComponent" is marked as standalone and can't be declared in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?"
Using the Angular-CLI in order to generate resulted in a component with a test file which is built upon ComponentFixture.
How can we make it possible to test a standalone component using Spectator?
Depends on your spectator version (mine is 10.0.0) but you can use the declareComponent property :
const componentFactory: SpectatorFactory<OurComponent> = createComponentFactory({
component: OurComponent,
declareComponent: false,
});

Change mockimplementation on specific functions within a manual mocked service

I have a service.
export const PostService = jest.fn().mockReturnValue({
findPost: jest.fn().mockResolvedValue(false),
updatePosts: jest.fn().mockResolvedValue(false),
});
I import the service into my (nestjs) test module and mock it.
import { PostService } from '../post.service';
jest.mock('../post.service')
const module: TestingModule = await Test.createTestingModule({
controllers: [PostController],
providers: [PostService]
}).compile();
postService = module.get<PostService>(PostService);
I want to change the implementation of functions inside the mocked postService for different tests.
test('findPost should return false', () => {
postController.getPost() // This calls postService.findPost(), which returns false
})
test('findPost should return true', () => {
// I'm trying to change the implementation here, and then calling the controller
postService.findPost.mockImplementation(() => true) // Error: mockImplementation is not a function
postController.getPost() // This should call postService.findPost() and return true
})
How can I change the implementation of any of the functions inside the mocked service depending on the test cases? For example, if I want to test a service method that throws an error depending on the parameters.
Been testing for two weeks, reading the jest docs, trying jest.doMock, messing around with the factory parameter, and importing the service per test and mocking it per test case. All the examples I could find of changing the mockImplementation per test case is for a single mocked function, not a jest function returning an object that contains multiple functions.
It turns out the solution is simple, all I needed was:
jest.spyOn("service, "method").mockImplementation(() => { implementation... })
This can change the implementation of any mock function in any test cases.
I am usually do it like this
const serviceMock = jest.fn(() => ({
methodMock(): () => { ... }
})
Then, in beforeEach function add this service as a provider
const module: TestingModule = await Test.createTestingModule({
controllers: [MyController],
providers: [
{
provide: MyService,
useValue: serviceMock,
},
],
}).compile();
controller = module.get<MyController>(MyController);
And if i want to do this only for some of the test cases, i just add this code to testcase. If i need to use it in a bunch of test cases, i wrap it in a function

Dependency injection issue for `#ntegral/nestjs-sentry` package in nestjs app

I am have an issue with this package #ntegral/nestjs-sentry in nestjs. I have a custom logger I use in my application
#Injectable()
export class CustomLogger implements LoggerService {
constructor(#InjectSentry() private readonly client: SentryService) {}
log(message: any, ...optionalParams: any[]) {
this.client.instance().captureMessage(message, ...optionalParams);
}
}
I then inject the into User Controller and in the user.controller.spec.ts
describe('UsersController', () => {
let controller: UsersController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [
CustomLogger,
UsersService,
SentryService,
],
}).compile();
controller = module.get<UsersController>(UsersController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
I get this error
FAIL src/users/users.controller.spec.ts (9.449 s)
● UsersController › should be defined
Nest can't resolve dependencies of the CustomLogger (?). Please make sure that the argument Symbol(SentryToken) at index [0] is available in the RootTestModule context.
Potential solutions:
- If Symbol(SentryToken) is a provider, is it part of the current RootTestModule?
- If Symbol(SentryToken) is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing Symbol(SentryToken) */ ]
})
I have tried adding the SentryService to the spec providers but that does not fix the error. Has anyone encountered this and how did you fix it.
I run in exactly the same issue. It seemed to be that the library uses a different token for its own Inject annotation. I was able to fix it in my tests by using the provided token for the SentryService mock.
import { SENTRY_TOKEN } from '#ntegral/nestjs-sentry';
// ...
const module: TestingModule = await Test.createTestingModule({
providers: [
// ...
{
provide: SENTRY_TOKEN,
useValue: { debug: jest.fn() }, // provide SentryService Mock here
},
],
})

Login Test with Ember Test

I have write a ember test file for a login formular. The redirect Page is a Dashboard with a Time Ticker Sidebar. Now, when i tested this, i became a test time out after 60000ms. Can i exclude the Time Ticker Component in ember test?
My Test Code:
import {test} from 'qunit';
import moduleForAcceptance from 'frontend/tests/helpers/module-for-acceptance';
moduleForAcceptance('Acceptance | Login', {
integration: true
});
test('visiting /user_session/new', function (assert) {
visit('/user_session/new');
fillIn('input#login-email', 'test#example.de');
fillIn('input#login-password', 'blabla');
click('button.btn-primary');
let done = assert.async();
andThen(() => {
Ember.run.later(null, () => {
assert.equal(currentURL(), '/dashboard', 'redirects to the dashboard');
done();
}, 1000);
});
});
The Time Ticker Component:
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'time',
date: moment().format('DD.MM.YYYY'),
time: Ember.computed('value', function() {
return moment().format('HH:mm:ss');
}),
didInsertElement: function() {
this.tick();
},
tick: function() {
this.set('nextTick', Ember.run.later(this, function() {
this.notifyPropertyChange('value');
this.tick();
}, 1000));
},
willDestroyElement: function() {
Ember.run.cancel(this.get('nextTick'));
}
});
Wrap ticking mechanism in a service and use that service in your component.
During test, mock that service and inject the mock in your component. Although mocking in acceptence test is not a good thing for me, I can go with mocking approach in this scenario.
Also you can consider using ember-moment's live update in your pages. It provides the live update of time. Here there is.

Test that a value is correctly set before calling an asynchronous service in a component

I'm writing unit tests for an Angular2 app in which a component is calling an asynchronous service when it is initialized in order to load its data. Before loading data, it should set a loading flag to true in order to show a spinner, and then the loading flag is set back to falseonce the data has been retrieved.
ngOnInit() {
this.reloadEmployees(this.filter);
}
reloadEmployees(filter: string) {
this.loading = true;
this.employeeService.getEmployees(filter).subscribe((results: Employee[]) => {
this.employees = results;
this.loading = false;
});
}
Here is how I wrote my test:
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [EmployeeComponent],
imports: [FormsModule, SpinnerModule, ModalModule, TranslateModule.forRoot()],
providers: [
{ provide: EmployeeService, useValue: employeeServiceStub },
]
});
fixture = TestBed.createComponent(EmployeeComponent);
component = fixture.componentInstance;
let employeeService = fixture.debugElement.injector.get(EmployeeService);
spy = spyOn(employeeService, 'getEmployees').and.returnValue(Observable.of(testEmployees));
});
it('should start loading employees when the component is initialized', fakeAsync(() => {
fixture.detectChanges();
expect(component.loading).toEqual(true);
}));
I was expecting the callback from the service to be run only if I call tick() in my test but apparently it is called anyway because component.loadingis already back to false when I check its value. Note that if I comment out the line that sets loading back to false in the callback of the component, the test passes.
Any idea how I should test that?
Rx.Observable.of seems to be synchronous (GitHub issue). That's why fakeAsync wrapper doesn't work with it.
Instead you can use e.g. Observable.fromPromise(Promise.resolve(...)).
In your case:
spy = spyOn(employeeService, 'getEmployees').and.returnValue(
Observable.fromPromise(Promise.resolve(testEmployees)));
Alternatively you can use async scheduler:
import { Scheduler } from 'rxjs/Rx';
...
spy = spyOn(employeeService, 'getEmployees').and.returnValue(
Observable.of(testEmployees, Scheduler.async));
I have prepared a working test sample on Plunkr