What are some ways to control providers in targeted tests using ng-mocks?

I recently started utilizing ng-mocks to streamline my testing process. However, I am struggling to figure out how to modify the value of mock providers in nested describes/tests after MockBuilder/MockRender have already been defined.

Specifically, my question is: Is it possible to change the value of mock providers in nested describes/tests post MockBuilder/MockRender setup?

In my application, I often encounter scenarios where components depend on services that offer different data based on the context. Testing this when the data is static is straightforward. Let's take ActivatedRoute as an example:

const mockActivatedRoute = { snapshot: { params: { id: 1 } };

describe('MyComponent', () => {
  MockInstance.scope('all');

  beforeEach(() => {
    return MockBuilder(MyComponent, MyModule)
      .provide({ provide: ActivatedRoute, useValue: mockActivatedRoute })
  });

  beforeEach(() => {
    fixture = MockRender(MyComponent);
    component = fixture.point.componentInstance;
  });

  // Assume the component has a routeId provided by the ActivatedRoute
  it('should have an id of 1', () => { expect(component.routeId).toEqual(1); });
});

So far so good.

However, following this test, I want to run another suite that alters the service; perhaps now the id should be changed to 2. I assumed that MockInstance would allow me to modify the provided services dynamically for individual tests.

To extend on my example, I will include a nested describe within the initial one after the first test:

describe('MyComponent', () => {
  ...

  describe('when initialized with a different id', () => {
    MockInstance.scope();

    beforeEach(MockInstance(mockActivatedRoute, () => {
      snapshot: { params: { id: 2 } }
    }));

    it('should have an id of 2', () => { expect(component.routeId).toEqual(2); });
  });
});

This test fails because the new mock value was not applied, and the routeId remained unchanged.

While I understand this example may not be ideal, I have attempted various approaches like using MockInstance.remember or attempting to initialize the service instance itself without success. It seems that MockBuilder and MockInstance might not be intended to be used together, as there are no documented examples on the ng-mocks website. It appears that creating separate describes with their own MockBuilder and MockRender may be the only way to handle such scenarios.

Alternatively, would it be best to make routeId public and directly manipulate it?

Answer №1

MockBuilder can be utilized in conjunction with MockInstance. It's crucial to acknowledge that MockInstance functions with services that have not yet been initialized. Essentially, it takes effect prior to MockRender or TestBed.createComponent.

Once either of them has been invoked, you need to obtain instances through TestBed.inject and directly mock them, for example, using ngMocks.stub and ngMocks.stubMember.

const service = TestBed.inject(Service);
let value: any;
ngMocks.stubMember(service, 'name', v => (value = v), 'set');
// value === undefined
service.name = 'fake';
// value === 'fake'

In your second test scenario, if you want to modify ActivatedRoute, make sure to utilize it rather than mockActivatedRoute. Additionally, since you are reusing TestBed across tests, it is advisable to depend on TestBed.inject instead of MockInstance:

beforeEach(() => {
  const activatedRoute = TestBed.inject(ActivatedRoute);
  ngMocks.stub(activatedRoute, {
    snapshot: {
      params: {
        id: 2,
      },
    },
  });
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Filtering strings with the same suffix

Here is a code snippet I am working with: type RouterQuery = keyof AppRouter['_def']['queries']; This code defines the following type: type RouterQuery = "healthz" | "post.all" | "post.byId" | "catego ...

The 'url' property is not found on the 'ActivationEnd' type in Angular

I have a service within my Angular application featuring the following constructor: constructor(private router: Router, private titleService: Title) { this.activeMenuItem$ = this.router.events.pipe( filter(event => event instanceof N ...

The successful operation of 'Ionic serve --lab' may involve the need to manually save the app.module

We are currently working on an Ionic project that consists of several pages and a single custom provider named request.ts. The issue we are facing is that whenever we launch it using the command ionic serve --lab, the compilation fails (with the error poin ...

Is there a way to seamlessly transition between different Angular components without having to refresh the entire webpage?

I'm currently working on implementing a navigation bar that allows users to switch between three components without having the navbar reload. The goal is for only the new component to load when the user clicks on a different section of the navbar, kee ...

Tips for creating unit tests for a controller that relies on a factory service that in turn relies on the $http service

I've been grappling with the concept of unit testing in AngularJS using Jasmine and Karma for a simple app. Despite my attempts to search online, I haven't been able to make much progress, especially when it comes to mocking and injecting a facto ...

Best practice for incorporating the cq-prolyfill third-party JavaScript library into an Angular 5 application

I'm experiencing an issue with the cq-prolyfill library not functioning properly when included through a typescript import statement within an angular module. I have confirmed that it is included in my vendor bundle, but for some reason the initial se ...

Is there a way to utilize a nearby directory as a dependency for a separate Typescript project?

I am working with a repository that has the following structure in typescript: . ├── common ├── project_1 └── project_2 My goal is to have the common package be used by both project_1 and project_2 as a local dependency. I am looking for ...

The element ion-virtual-scroll is unrecognized

<ion-virtual-scroll [items]="churchNewsList" approxItemHeight="120px"> <ion-item *virtualItem="let news"> {{ news }} </ion-item> </ion-virtual-scroll> I encountered an issue with the following error messag ...

An issue occurred when clicking on a line due to the filter

Issue at Hand: Currently, I am facing a problem where selecting an item from the list by clicking on the button leads me to access the information of a different item when the filter is applied. Desired Outcome: I wish to be able to access the correct inf ...

The MUI component received props that were not defined

I created a customized MUI card with the intention of applying a dark background when the darkBg prop is passed. However, I've encountered an issue where despite passing darkBg as true, the card's background remains white. To troubleshoot, I atte ...

Compiling async code with generators in Typescript proves to be challenging

Scenario As I delve deeper into Typescript, I've come across the advice that blocking calls should not be made within asynchronous code. I have also found generators to be helpful in simplifying directory traversal and preventing stack overflow. ...

TypeScript encountered an unexpected { token, causing a SyntaxError

Despite multiple attempts, I can't seem to successfully run my program using the command "node main.js" as it keeps showing the error message "SyntaxError: Unexpected token {" D:\Visual Studio Code Projects\ts-hello>node main.js D:&bsol ...

Assign a predetermined value to a dropdown list within a FormGroup

I have received 2 sets of data from my API: { "content": [{ "id": 1, "roleName": "admin", }, { "id": 2, "roleName": "user", }, { "id": 3, "roleName": "other", } ], "last": true, "totalEleme ...

The type 'typeof globalThis' does not have an index signature, therefore the element is implicitly of type 'any'. Error code: ts(7017) in TypeScript

I'm encountering an issue with my input handleChange function. Specifically, I am receiving the following error message: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.ts(7017) when att ...

Tips for utilizing the @Input directive within the <router-outlet></router-outlet> component

I am new to Angular 4 and recently discovered that in Angular, data can be passed from a parent component to a child component using @Input like this: <child [dataToPass]="test"></child> My question is, how can I achieve the same functionalit ...

Issues with Angular Form Builder's Form Control not updating when invalid, causing errors with ngIf statements

I am encountering an issue where the error division fails to appear when the validator is invalid. The problem seems to be originating from this line: <div class = "danger" *ngIf="firstName?.invalid && (firstName?.dirty || firstName?.touched) ...

ERROR: PipeError - Conversion of "Invalid Date" to a date is not possible for the DatePipe

While attempting to format a date with time, I ran into an error. The way I am sending the request is as follows: created = this.datePipe.transform(dateCreated, 'yyyy-MM-ddTHH:mm'); I require the time in order to consume a service that necessi ...

A guide on updating a MySQL table using a JSON object in Node.js

I have a JSON Object and need to UPDATE a mySQL table without listing all of the keys individually For an INSERT operation, I use the following code: var arrayValue = Object.keys(obj).map(function(key) { return String("'"+obj[key]+"'"); ...

operating efficiently even when producing an incorrect output type

Exploring Typescript for the first time on Codepen.io has left me puzzled. I'm unsure why, despite defining the function signature and return type with an interface, I am able to return a different type without encountering any errors. Is there somet ...

Maintaining the order of the returned values type is crucial when working with mapped objects in Typescript

Currently, I am developing a basic mapper function for objects. This function is designed to take an array of object properties and then return an array containing the corresponding values of these properties. The function works as intended; however, I hav ...