crypto news

Here is what the positives do not tell you about the angle unit test

introduction

In this article, I would like to share the experience you gained over the years with the unit test in Angular. In short, I will talk about the following:

  • Why should you test the unit?
  • Why should you harness and what are the advantages/defects?
  • What is sifers and why should you care?
  • What is the angular test library (ATL)?
  • Test using sifers
  • Inquire about DOM elements and send events
  • What is JEST-Auto-Spies and Observer-SPY?

Why?

I have seen many applications that do not contain any unit tests. Let me explain why we need to write unit tests. Unit tests are an essential aspect of any application. It provides us with confirmation and confirmation about how the blog behaves. It also serves as documents to understand what the symbol does. Writing good tests that help us understand the symbol design. The inability to write a unit test, indicates a bad design and usually tells us to reshape the code.

The more your tests are similar to the way your program is used, the more they can present it.

Make fun of

To ensure that you can focus on the code that must be tested, you must mock the external dependencies properly. For example, you should make fun of any other services or components that you experience. It is not recommended to import real applications (more about the reason below). Do not hesitate to import pure ingredients, though, if the component uses it as clouds. You can also import a common unit containing all its dependencies.

Disadvantages of irony

  • You will use real implementation and be forced to ridicule all its characteristics, methods, etc. You will end up in a rabbit hole, as you suddenly make fun of the chapters that several layers below the dependency tree.
  • You will have to announce the intertwined ingredients and provide all their dependencies
  • The implementation of your tests takes longer because the full dependency tree must be solved first.
  • Your test condition may not be correct.
  • Your tests will start suddenly if the dependency changes in the direction of the river.
  • It becomes very difficult to correct tests when an error occurs.

sifers

Let’s start preparing. Instead of using beforeEach To prepare the test environment, use sifers.

SImple IImprovised andUncies EXPLICITED Petuuring STATE (SIFERS) is a way to capture what tests should do when preparing the test environment as well as a clean changeable condition.

Sifers use a setup The function that can receive optional broker to prepare the test environment. This is the biggest benefit compared to beforeEach. beforeEach It is called automatically before each unit test, which hinders us from setting any march values ​​on the required dependencies while preparing the component/service.

It allows us to use SIFERS to be much more flexible with the test environment for ridicule of values ​​before preparing the component/service.

the setup Then the job is called in each test and it can return a test for the test (chapters, characteristics, etc.).

One thing I like about Siffers is trying to keep the number of small arguments. If you need several mediators or your list grows on a certain number of parameters, you can use an interface. This will keep your symbol organized and easy to read.

basic setup The job can look like this:

function setup({ value = false }) {
  const mockService: Partial<RealService> = {
    someFunction: jest.fn()
      .mockReturnValue(value ? 'foo' : 'bar'),
  };

  const service = new MyService(mockService);
  return {
    service,
    mockService,
  };
}

Using the above example, the tests can look like this:

it('returns foo when feature flag is enabled', () => {
  // Pass true into the setup to ensure that 
  // someFunction returns foo
  const { service } = setup(true);
  expect(service.someFunction()).toEqual('foo');
});

it('returns bar when feature flag is disabled', () => {
  // Pass false into the setup to ensure that 
  // someFunction returns bar
  const { service } = setup(false);
  expect(service.someFunction()).toEqual('bar');
});

I am not going into the full details of Sives here because it was already explained by the author Moshe Kulodni.

Test with sifers

Angle test library (ATL)

I am a big fan of ATL library and I try to use it in all my projects. ATL is a very lightweight solution for angular ingredients testing. ATL is described as follows:

The angular test library provides the functions of the tool to interact with the corner components, in the same way as the user.

Tim Decrever

Let’s start preparing the stereotype. Instead of using TestBed.configureTestingModuleYou need to use render road. Keep in mind that render The method should be used only if you are testing Elements. Services can be tested without atl and render road.

There are many examples of how to use ATL here. It contains everything from ingredients, models, inputs/output, NGRX, directions, corner materials, signs, etc. Tim Deschryer also has a very detailed article with many examples recommended by reading.

Here is an example using a SIFERand render road. You will also notice that I am using createSpyFromClass A way to ridicule categories, which automatically harness all functions, characteristics and even notes for us automatically. More on it is later covered in the article.

import { render } from '@testing-library/angular';
import { createSpyFromClass } from 'jest-auto-spies';
// ... other imports

async function setup({ enableFlag = false }) {
  const mockMySomeService = createSpyFromClass(MyService);
  mockMySomeService.doSomething.mockReturnValue(enableFlag);

  const { fixture } = await render(AppComponent, {
    imports: [...],
    providers: [{ 
      provide: MyService, 
      useValue: mockMySomeService 
    }],
  });
}

Determination of ads

Like TestBedYou can pass a set of ingredients and directions with ads. The construction of the sentence is the same.

However, if you are importing a stereotype, this already contains the ingred true. Another useful feature that you may need to use in your ATL API tests. See the full application programming interface for examples.

Preparation of service providers

Use componentprovides to set service providers for your component.

Set @Enter/ @Directing

situation @Input and @Output The ingredient properties can be achieved using componentproperties. This allows you to appoint them at the same time.

If you need more control of these properties, you can use ingredients or components. in TestBed An existing test, you may only set the entry through the same component.

Test services

To test the services, you do not need to use ATL or TestBed. Instead, you can pass the easy dependencies directly to the service facility to be tested. The example below makes fun of LogService and TableService.

// some.service.ts
@Injectable({ providedIn: 'root' })
export class SomeService {
  constructor(
    private readonly logService: LogService, 
    private readonly tableService: TableService) {}
}

// some.service.spec.ts
async function setup() {
  const mockLogService = createSpyFromClass(LogService);
  const mockTableService = createSpyFromClass(TableService);

  const service = new SomeService(
    mockLogService, 
    mockTableService
  );

  return {
    service,
    mockLogService,
    mockTableService,
  };
}

Test components

The component should always test the behavior of the public application programming interface. Private application programming facades are never explicitly tested. To test the ingredients, use DOM as much as possible. This is the same behavior you expect from your user and you want to simulate the test as much as possible. ATL helps us in this. This is also called the shallow test.

Not all general methods of your component are treated as a public application programming interface that you can test directly from your unit tests. They are only public in your template. The public roads are called from DOM (i.e. click button) and must be tested in the same way.

An example of a component to be tested:

// app-foo.component.ts
@Component({
  selector: 'app-foo',
  template: `
    <input 
      data-testid='my-input'
      (keydown)='handleKeyDown($event)' />`
})
export class FooComponent {
  constructor(private readonly someService: SomeService) {}

  handleKeyDown(value: string) {
    this.someService.foo(value);
  }
}

Your Sepher setup You can look like this:

// app-foo.component.spec.ts
async function setup() {
  const mockSomeService = createSpyFromClass(SomeService);
  const { fixture } = await render(FooComponent, {
    providers: [{ 
      provide: SomeService, 
      useValue: mockSomeService 
    }],
  });

  return {
    fixture,
    mockSomeService,
    fixture.componentInstance
  }
}

Do not do this. The test gets directly to the public application programming interface, completely transcending the template. You can remove dom input The entire element and the test will continue to pass. This is a wrong positive test and does not serve any purpose.

it('emits a value', async () => {
  const { mockSomeService, component } = await setup(...);
  component.handleKeyDown(value);

  expect(mockSomeService.foo)
    .toHaveBeenCalledWith(value);
})

This is the correct way to test the job. Here I am using screen To reach input The element and userEvent To remove the events of Dom.

import { screen } from '@testing-library/angular';
import userEvent from '@testing-library/user-event';

it('emits a value', async () => {
  const { mockSomeService, component } = await setup(...);
  const textbox = screen.queryByTestId('my-input');

  userEvent.type(textbox, 'foo,');
  userEvent.keyboard('{Enter}');

  expect(mockSomeService.foo)
    .toHaveBeenCalledWith(value);
})

DOM elements query using screen

the screen API provides many powerful functions to inquire about DOM. Jobs like waitFor or findBy Repeat a promise and can be used to find its clay -changing elements on the basis of some circumstances.

It is recommended to inquire about the elements in the following order. See API for a full priority list and descriptions.

  1. Getbyrole
  2. Getbylaltext
  3. Getbyplaceholdrtext
  4. Getbytext
  5. Getbydisplayvalue
  6. Getbyttext
  7. Getbytitle
  8. Getbytestid

Send DOM procedures

ATL comes with two application programming facades to send events through DOM:

userEvent He prefers fireEvents (Introduced from Events API). The difference as stipulated in the documents is:

Fireefent sends DOM events, while the user event simulates full reactions, which may launch multiple events and do additional checks along the way.

JEST-Auto-Spies

To ridicule the seasons, I am using jest-auto-spies. jest-auto-spies Return a safe category of the permitted type without the need to define all its functions and characteristics manually. Besides saving a lot, it also provides assistant functions for monitoring, methods, metal and receptors. The example below is the use of a file createSpyFromClass A way to create a spy category.

If you need to provide dependency on your stereotypes directly, you can use it provideAutoSpy(MyClass)It is an abbreviation of {provide: MyClass, useValue: createSpyFromClass(MyClass)}.

Keep in mind that this should be used only if you do not need to ridicule any functions for that category. If you need to ridicule something of this category, you should provide an indignant counterpart.

Here are some examples:

Create a simple spy in the chapter

const mockMyService = createSpyFromClass(MyService);

Create a spy on the chapter and send value on

const mockMyService = createSpyFromClass(MyService, {
    observablePropsToSpyOn: ['foo$'],
});

mockMyService.foo$.nextWith('bar');

Create a spy on the chapter and the job

const mockMyService = createSpyFromClass(MyService, {
    methodsToSpyOn: ['foo'],
});

mockMyService.foo.mockReturnValue('bar');

Sees jest-auto-spies For more assistant function and use.

Use a SPY observer instead of subscribing / avoiding done Relax again

To test the unsafe code, I use it subscribeSpyTo from observer-spy A library instead of subscribing to the note. This also helps get rid of done Call again.

the done The function was presented to test the Async icon. However, it is very vulnerable and unpredictable. Because of this, you may get false positives that make your tests green, while you may get another time.

Note that there is a lint base that you can use to prohibit use done Call again.

Also, you do not need to cancel the subscription from the observation, as there is a place to cancel the automatic subscription. This is called automatically in afterEach.

Here are some examples of how the library is used from Readme. See more examples in Readme.

const fakeObservable = of('first', 'second', 'third');
const observerSpy = subscribeSpyTo(fakeObservable);

// No need to unsubscribe, as the have an auto-unsubscribe in place.
// observerSpy.unsubscribe();

// Expectations:
expect(observerSpy.getFirstValue()).toBe('first');
expect(observerSpy.receivedNext()).toBeTruthy();
expect(observerSpy.getValues()).toEqual(fakeValues);
expect(observerSpy.getValuesLength()).toBe(3);
expect(observerSpy.getValueAt(1)).toBe('second');
expect(observerSpy.getLastValue()).toBe('third');
expect(observerSpy.receivedComplete()).toBeTruthy();

resources

summary

The article explored my experience with the unit test in Angular, while emphasizing its importance of the quality of the code and its maintenance. It covered the necessary irony to isolate the units and avoid relying on real applications. Sifers are highlighted as a flexible approach to preparing the test environment and enhance the testing of the test. The angular test library (ATL) was presented as a valuable tool for components testing, simulating user reactions as soon as possible for real users. In addition, the article mentioned useful tools such as Jest-Auto-Spies to ridicule an effective category and provide resources for more exploration for test practices in Angular.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button

Adblock Detected

Please consider supporting us by disabling your ad blocker