Learning Objectives

By the end of this article, you'll learn how to:

HTML Template

<input
  id="myFile"
  type="file"
  (change)="onFileSelected()"
  #fileInput
/>

Whenever the user selects a file, the change event calls the onFileSelected() method.

Component

import { Component } from '@angular/core';

@Component({
  selector: 'app-input-file',
  templateUrl: './input-file.component.html'
})
export class InputFileComponent {

  uploadedFile!: File;

  onFileSelected(): void {

    const inputNode = document.querySelector('#myFile') as HTMLInputElement;

    if (inputNode.files && inputNode.files.length > 0) {
      this.uploadedFile = inputNode.files[0];
      console.log(this.uploadedFile);
    }

  }

}

How It Works

When the user selects a file:

Although this works, Angular recommends avoiding direct DOM access where possible.

Writing the Unit Test

The browser normally populates the files property, so during unit testing we need to mock it ourselves.

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputFileComponent } from './input-file.component';

describe('InputFileComponent', () => {

  let component: InputFileComponent;
  let fixture: ComponentFixture<InputFileComponent>;

  beforeEach(async () => {

    await TestBed.configureTestingModule({
      declarations: [InputFileComponent]
    }).compileComponents();

    fixture = TestBed.createComponent(InputFileComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();

  });

  it('should store the selected file', () => {

    const file = new File(
      ['Dummy Content'],
      'sample.txt',
      {
        type: 'text/plain'
      }
    );

    const input = fixture.nativeElement.querySelector('#myFile');

    Object.defineProperty(input, 'files', {
      value: [file]
    });

    input.dispatchEvent(new Event('change'));

    expect(component.uploadedFile).toEqual(file);

  });

});

Understanding the Test

Step 1: Create a Mock File

const file = new File(
  ['Dummy Content'],
  'sample.txt',
  {
    type: 'text/plain'
  }
);

This creates a fake File object that behaves exactly like a file selected by the user.

Step 2: Mock the Browser's files Property

Object.defineProperty(input, 'files', {
  value: [file]
});

Since the browser owns the files property, we replace it with our mock file during testing.

Step 3: Simulate File Selection

input.dispatchEvent(new Event('change'));

This triggers the same event that occurs when a user selects a file.

Step 4: Verify the Result

expect(component.uploadedFile).toEqual(file);

The test passes if the component correctly stores the selected file.

A Better Angular Approach

Instead of querying the DOM with document.querySelector(), Angular encourages passing the event object directly.

Template

<input
  type="file"
  (change)="onFileSelected($event)"
/>

Component

onFileSelected(event: Event): void {

  const input = event.target as HTMLInputElement;

  if (!input.files?.length) {
    return;
  }

  this.uploadedFile = input.files[0];

}

This approach is:

Unit Test for the Improved Version

it('should store the selected file', () => {

  const file = new File(
    ['Angular Testing'],
    'document.pdf',
    {
      type: 'application/pdf'
    }
  );

  const event = {
    target: {
      files: [file]
    }
  } as unknown as Event;

  component.onFileSelected(event);

  expect(component.uploadedFile).toEqual(file);

});

Notice that we no longer need to manipulate the DOM. We simply create a mock event object and call the component method directly, making the unit test cleaner and easier to understand.

Conclusion

Unit testing file uploads in Angular is straightforward once you know how to mock the browser's files property. While older implementations often relied on document.querySelector(), modern Angular applications should use the event object passed by the change event. This results in cleaner code, simpler unit tests, and components that are easier to maintain.

The overall testing strategy is simple:

Following this approach will help you confidently test file upload functionality in any Angular application.