3
votes

I am trying to unit test a "add object" (add dish) component in Angular 6. With my current code I get the error "Cannot read property 'dishForm' of undefined".

So, it seems it does the DishAddComponent is not recognized as the component to be tested? Can anybody help me out with this? Thanks in advance!

I've also tried (amongst others) adding async to beforeEach, as described here: Angular 2 Testing - Component Instance is undefined , but it does not work.

Angular Component to be Unit Tested:

export class DishAddComponent implements OnInit {
  public dishForm: FormGroup;

  constructor(private dishService: DishService,
    private formBuilder: FormBuilder,
    private router: Router) { }

  ngOnInit() {
    // define dishForm (with empty default values)
    this.dishForm = this.formBuilder.group({
      name: ['', [Validators.required, Validators.maxLength(30), Validators.pattern('[a-zA-Z :]*')]],
      country: [''],
      ingredients: this.formBuilder.array([]),
      recipe: ['', [Validators.required, Validators.minLength(50)]],
    }, { validator: CustomValidators.DishNameEqualsCountryValidator });
  }

  addIngredient(): void {
    let ingredientsFormArray = this.dishForm.get('ingredients') as FormArray;
    ingredientsFormArray.push(IngredientSingleComponent.createIngredient());
  }

  addDish(): void {
    if (this.dishForm.dirty && this.dishForm.valid) {

      let dish = this.dishForm.value;

      this.dishService.addDish(dish)
        .subscribe(
          () => {
            this.router.navigateByUrl('/dishes');
          });
    }
  }
}

Unit Test in Jest:

describe('DishAddComponent', () => {
  let component: DishAddComponent;
  let fixture: ComponentFixture<DishAddComponent>;
  let formGroup: FormGroup;
  let formBuilderMock: any;
  let dishServiceMock: any;

  beforeEach(() => {
      formBuilderMock = {
      group: jest.fn().mockName('formBuilderMock.group'),
      array: jest.fn().mockName('formBuilderMock.array')
    };

    dishServiceMock = {
      addDish: jest.fn().mockName('dishServiceMock.addDish')
    };

    TestBed.configureTestingModule({
      imports: [ ReactiveFormsModule, FormsModule, RouterTestingModule.withRoutes(routes)],
      declarations: [ DishAddComponent, IngredientSingleComponent ],
      providers: 
      [ 
        { provide: FormBuilder, useValue: formBuilderMock },
        { provide: FormGroup, useValue: formGroup },
        { provide: DishService, useValue: dishServiceMock }
      ]
    });
    fixture = TestBed.createComponent(DishAddComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('form invalid when empty', () => {
    (console).log(component);
  expect(component.dishForm.valid).toBeFalsy();
 });
});

EDIT: If I use the code below, I get an error as well:

DishAddComponent › should create

expect(received).toBeTruthy()

Received: undefined

  it('should create', () => {
    expect(component).toBeTruthy();
  });
2
Try using the default test, which is should create → expect(component).toBetruhty();. See if it works. Also, please post the error you're facing.user4676340
Thanks for your quick response! I've editted my question. If I change it to the default test, I get the following error: ● DishAddComponent › should create ● expect(received).toBeTruthy() ● Received: undefinedRobin

2 Answers

2
votes

In the end I got rid of the "undefined error" with the code below. I needed to mock one of the childcomponents and create an instance of formbuilder. Thanks for the tips!

describe('DishAddComponent', () => {
  let component: DishAddComponent;
  let fixture: ComponentFixture<DishAddComponent>;
  let dishServiceMock: any;

  // mock singleingredientcomponent
  @Component({
    selector: 'app-ingredient-single',
    template: '<div></div>',
  })
  class MockSingleIngredientComponent {
  @Input() public ingredientIndex: number;
  @Input() public ingredient: FormGroup;
  }

  // create new instance of FormBuilder
  const formBuilder: FormBuilder = new FormBuilder();

  beforeEach(async(() => {
    dishServiceMock = {
      addDish: jest.fn().mockName('dishServiceMock.addDish'),
      addIngredient: jest.fn().mockName('dishServiceMock.addIngredient')
    };

    TestBed.configureTestingModule({
      imports: [ ReactiveFormsModule, FormsModule, RouterTestingModule.withRoutes([])],
      declarations: [ DishAddComponent, MockSingleIngredientComponent ],
      providers: 
      [ 
        { provide: FormBuilder, useValue: formBuilder },
        { provide: DishService, useValue: dishServiceMock }
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    // create component and test fixture
    fixture = TestBed.createComponent(DishAddComponent);

    // get test component from the fixture
    component = fixture.componentInstance;

    // ..
    fixture.detectChanges();
  });

  // unit tests
  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
1
votes

Try to compile components by adding compileComponents() after configureTestingModule()

TestBed.configureTestingModule({
  imports: [ ReactiveFormsModule, FormsModule, RouterTestingModule.withRoutes(routes)],
  declarations: [ DishAddComponent, IngredientSingleComponent ],
  providers: 
  [ 
    { provide: FormBuilder, useValue: formBuilderMock },
    { provide: FormGroup, useValue: formGroup },
    { provide: DishService, useValue: dishServiceMock }
  ]
})
  .compileComponents();