Ways to verify the functionality of a function utilizing a Subscription that yields no return value

I'm working with a TypeScript ModelService that has an edit function:

edit(model: Model): Observable<Model> {
  const body = JSON.stringify(model);
  return this.http.put(`/models/edit/${model.id}`, body));
}

Additionally, there is a TypeScript EditComponent that contains an edit function. This function subscribes to the service and navigates when it receives a response:

edit(model: Model): void {
  this.service
    .edit(model)
    .subscribe(() => this.router.navigate([`/models/details/${model.id}`]);
}

What would be the most effective method for testing this component's edit function?

A Jasmine test that I have attempts to achieve this:

// Set up
TestBed.configureTestingModule({
  declarations: [EditComponent],
  providers: [
    {
      provide: ModelService,
      useValue: jasmine.createSpyObj('ModelService', ['edit'])
    }
  ]
});
const fixture = TestBed.createComponent(EditComponent);
const component = fixture.componentInstance;
const modelService = fixture.debugElement.injector.get(ModelService);
fixture.detectChanges();

// Test
it('should call edit', () => {
  fakeAsync(() => {
    component.edit(model);
    expect(modelService.edit).toHaveBeenCalled();
  });
});

However, when running this test, I consistently receive SPEC HAS NO EXPECTATIONS. I believed fakeAsync ran synchronously, which led me to believe it would work.

I've also experimented with variations of async, tick(), and done(), but these approaches either yield the same message or fail with

Cannot read property 'subscribe' of undefined
when attempting to call the component's edit function.

In previous tests, I successfully used

return fixture.whenStable().then()
(as explained here), but in this case, I don't think it applies given that the component function returns void rather than a Promise.

Could anyone provide insight on a more efficient way to test this component function?

Answer №1

When dealing with SPEC HAS NO EXPECTATIONS

If you're receiving a message stating that there are no expectations in your code, it might be due to how you are using the fakeAsync function. Here's the correct way to structure your test:

it('should call edit', fakeAsync(() => {
  component.edit(model);
  expect(modelService.edit).toHaveBeenCalled();
});

In this scenario, you can consider the modelService.edit() call as something that can be expected synchronously since it is triggered within component.edit().

Therefore, you can simplify your test like this:

it('should call edit', () => {
  component.edit(model);
  expect(modelService.edit).toHaveBeenCalled();
});

Dealing with Cannot read property 'subscribe' of undefined

If you encounter an error related to not being able to read the property 'subscribe' of undefined, it may be because the spy object you created does not have any returns specified. As a result, modalService.edit() returns undefined, leading to this error. In such cases, you can stub the result by creating a new Observable or utilizing of() to provide suitable return values for the component to interact with.

jasmine.createSpyObj('ModelService', { edit: of() })

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

When passing an invalid value to the Props in TypeScript, no errors are being thrown

const enum ColumnValues { one = 1, two = 2, three = 3, } interface Props { style?: StyleProp<ViewStyle>; title?: string; titleContainerStyle?: StyleProp<ViewStyle>; titleStyle?: StyleProp<TextStyle>; textInputStyle?: Styl ...

Angular (2/4) application utilizing custom-named routing within a single page architecture

I'm currently working on an Angular application that consists of a login component and a home component which serves as the main handler for the entire single page application. Additionally, I have three more components named users, each with function ...

Guide to customizing the appearance of a component's host element on-the-fly

For instance: https://stackblitz.com/edit/angular-mkcfsd In my project, I have an icon component called app-icon which dynamically takes a path and inserts it into an SVG viewbox. I extract the height and width of the path, then set the SVG to match those ...

Mastering regular expressions in TypeScript

My goal is to perform linting on staged files that are either .ts or .tsx and located within the src folder. I am aware that for selecting all js files one can use "*.js": [--list of commands--] inside the lint staged property. I'm curious to learn m ...

Are there any methods to utilize Zod for validating that a number contains a maximum of two decimal places?

How can I ensure that a numeric property in my object has only up to 2 decimal digits? For example: 1 // acceptable 1.1 // acceptable 1.11 // acceptable 1.111 // not acceptable Is there a method to achieve this? I checked Zod's documentation and sea ...

Laravel 5.4 integrates with Angular4: Implementing Passport token requests

Trying to make a Token request from API using: Signin(email: string, password: string): void { let data: Object = { client_id: 2, client_secret: 'vSFxVqALQHjyotPyGfhrGj3ziudUGsts2ZWiAGms', grant_type: 'password&a ...

Is there a way to set the size of my unique carousel design?

Having some trouble with my modal image carousel; the dimensions keep shifting for different image sizes. Image 1 Image 2 ...

What is the necessity behind keeping makeStyles and createStyles separate in Material UI with TypeScript?

Dealing with TypeScript issues in Material UI has been a frequent task for me, and I find it frustrating that styling components requires combining two different functions to create a hook every time. While I could use a snippet to simplify the process, it ...

Emphasizing the chosen element in a list using angular

After retrieving an array of items from the database, my list is constantly changing, causing the HTML display to update dynamically. However, I am struggling with highlighting only the selected item in the list, as the entire list gets highlighted upon se ...

Reactive Programming: Transforming an earlier value as it moves down the pipeline

In a recent project, I encountered an interesting scenario involving the execution of multiple requests in a pipe chain. This specific case revolves around the display of images within the quill text editor. The backend returns the content in the followin ...

Tips on performing a join in an AngularFire2 database

I have been searching diligently but am still unable to find the correct answer. Any guidance towards the right direction would be greatly appreciated. My database contains information on users and projects: projects: 08177a0acb3f4d96f8cb5fa19002d2ed ...

Why does the implementation of my interface differ from what is specified in the TypeScript documentation?

Currently delving into the world of TypeScript documentation https://www.typescriptlang.org/docs/handbook/2/classes.html Specifically focusing on the section implements Clauses, an interesting revelation surfaces: A Word of Caution It’s worth noting t ...

The type 'string' cannot be assigned to type 'ImageSourcePropType'

Context Attempting to utilize a SVG component in React Native with the xlinkHref property within an Image tag. The SVG file was converted into a React Native Component (TSX) using the tool provided at . While simple SVG icons have been successfully used be ...

Issue encountered when attempting to establish a new loadingController with LoadingOption

Hey there, I am relatively new to Ionic and I encountered an issue while trying to create a new loading screen. Argument of type '{ content: string; }' is not assignable to parameter of type 'LoadingOptions'. Object literal may only s ...

Angular 2 components sharing a common route

Two modules share the same path but have different templates (main and auth). The modules should be loaded based on whether the user is authenticated or not. Unfortunately, this functionality is not currently working as intended. Below are the relevant co ...

Is there a way to execute browser.get just once in a script?

I'm completely new to protractor, and I've been setting up the browser.get(URL) command within my first 'it' statement. Then, in my afterEach statement, I navigate back to the homepage. I'm curious if there is a more efficient pla ...

In JavaScript, the function will return a different object if the string in an array matches the

My task involves working with a simple array of string ids and objects. Upon initial load, I am matching these Ids with the objects and setting the checked property to true. const Ids = ['743156', '743157'] [ { "id&quo ...

The attribute 'XXX' is not found within the type 'IntrinsicAttributes & RefAttributes<any>'

My coding hobby currently involves working on a React website using TypeScript. I recently came across a free Material UI template and decided to integrate it into my existing codebase. The only challenge is that the template was written in JavaScript, cau ...

Personalizing the arrow positioning of the Angular8 date picker (both top and bottom arrow)

I am interested in enhancing the design of the Angular 8 date picker by adding top and bottom arrows instead of the default left and right arrows. Can someone guide me on how to customize it? Check out the Angular 8 date picker here ...

An issue occurred: the channel has been terminated within the ChildProcess.target.send function at internal/child_process.js, line 562, character

My Angular 5 application is giving me an error message that says "ERROR in Error: channel closed". This occurs after running the application. The issue seems to persist even though it works fine for a few minutes after executing ng serve. Has anyone else e ...