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

Struggling to incorporate Dependency Injection into Angular 4

I have a class defined as shown below: import { Injectable, Inject } from '@angular/core'; @Injectable() export class MovieIndustry { constructor(private music: MusicIndustry) { } producer() { this.music.album(); al ...

Why isn't the Mat error functioning properly in this Angular code?

Could someone explain why the Mat error does not seem to be functioning properly in this Angular code snippet? <div class="form-group"> <mat-form-field class="row-styling"> <mat-label for="aplctnName"> Application Name <sp ...

Encountering difficulty adjusting the size of my charts when attempting to utilize the billboard.js chart library with Angular 6

I am currently working on integrating the billboard.js chart library with Angular 6, but I am encountering an issue with the size of the chart. Upon the initial page load, the chart appears larger than its containing div element. However, when I resize the ...

What is the best way to access the original observed node using MutationObserver when the subtree option is set to

Is there a way to access the original target node when using MutationObserver with options set to childList: true and subtree: true? According to the documentation on MDN, the target node changes to the mutated node during callbacks, but I want to always ...

Leverage Angular2 validators beyond FormControl's limitations

As I work on developing a model-driven form in Angular2 RC5, one of the features I am implementing is the ability for users to add multiple entries either manually or by importing from a file. To display the imported data in a table, I utilize JavaScript t ...

What is the best way to implement the useCallback hook in Svelte?

When utilizing the useCallback hook in React, my code block appears like this. However, I am now looking to use the same function in Svelte but want to incorporate it within a useCallback hook. Are there any alternatives for achieving this in Svelte? con ...

Assign a variable with the value returned by a function

Can you help me with this question I have about validating fields with a function using AbstractControl? errorVar: boolean = false function(c: AbstractControl): {[key: string]: string } | null { // validation if 'test' is true or not goes here ...

The compiler is still throwing missing return errors despite narrowing down all potential types

I encountered the following issue: Function is missing a closing return statement and its return type does not include 'undefined'. Here's my TypeScript code snippet: function decodeData( data: string | number[] | ArrayBuffer | Uint8Arr ...

Unable to access data from the Array by passing the index as an argument to the method

Having trouble retrieving an item from an Array using method() with an index argument that returns undefined export class DataService { public list = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, ...

Using Angular to Generate a File from Form Input and Delivering it to the User

I am attempting to develop a feature in Angular 9 that takes user input from a textarea, processes it, and then presents it back to the user as a downloadable (txt) file. The structure of the form in app.component.html is as follows: <form (ngSubmit)= ...

How can I target and focus on a dynamically generated form in Angular 4/Ionic3?

One of the challenges I'm facing is dealing with dynamically created forms on a page. Each row consists of inputs and a button. Is there a way to select/focus on the input by clicking on the entire row (button)? It should be noted that the number of r ...

Leveraging Firestore Errors within Cloud Functions

Is it possible to utilize the FirestoreError class within Firebase cloud functions? The goal is to raise errors with a FirestoreError type when a document or field is not found in Firestore: throw new FirestoreError(); Upon importing FirestoreError using ...

Angular's HttpClient makes sure to wait for the HTTP requests to complete

Initializing arrays with the call this.Reload.All() is causing confusion and breaking the service due to multiple asynchronous calls. I am looking for a synchronous solution where each call waits for its response before proceeding to the next one. How can ...

Adding a total property at the row level in JavaScript

Here is a JavaScript array that I need help with: [{ Year:2000, Jan:1, Feb: }, {Year:2001, Jan:-1, Feb:0.34 }] I want to calculate the total of Jan and Feb for each entry in the existing array and add it as a new property. For example: [{ Year:2000, Ja ...

Develop dynamic components in Angular using up-to-date methods

I currently have three components named PersonComponent, AddressComponent, and CompanyComponent all already defined. Is there a way to dynamically create each component when I have the component name as text, for example, "PersonComponent"? I have inject ...

Retrieving the attribute key from a dynamically typed object

Having this specific interface structure: interface test { [key: string]: string } along with an object defined as follows: const obj: test ={ name: 'mda', telephone: '1234' } Attempting to utilize this object in a variab ...

Accessing the total number of items from a service in Angular

I am facing an issue with my cart-service and 2 components. The product-list component is responsible for displaying the products along with a buy button. However, the top-bar component seems to have trouble displaying the count when I try to directly call ...

Is it possible to jest at a module function that is both exported and utilized within the same module?

Just diving into unit testing and learning about spies, stubs, and mocks. I've been trying to test the verify method in password.js as shown in the code snippet below. However, I'm having trouble creating a stub for the hash function within the ...

Angular 2 Release Candidate 6 form input pattern always fails to pass

How can I ensure that a required input in my form fails validation if there is no non-whitespace character present? Despite setting the pattern to [/S]+, the validation does not pass. Could I be missing an import or something else? My Template: <form ...

I'm looking for a way to merge the functionalities of tsc build watch and nodemon into a single Node.js

Currently, I have two scripts in my code: "scripts": { "build": "tsc -p . -w", "watchjs": "nodemon dist/index.js" } I need to run these two scripts simultaneously with one command so that the build ...