79075111

Date: 2024-10-10 15:22:20
Score: 1
Natty:
Report link

I think the answers you are looking for have to do with order of operations.

Why is that?

I view it this way

If your constructor (or the variables defined at component level) rely on values from your dependencies, your mocks will need to have those values prepared already for them to get assigned during construction.

For test setup ease, I tend to put all of my dependency assignments in the onInit method of my component. This allows me to grab the mock inside of my it and setup any changed data, then call fixture.detectChanges to have the component use those values.

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let compiled;
  // I usually declare my mocks here so that I can assign right after the configureTestingModule call

  beforeEach(async () => {
    const spy = jasmine.createSpyObj('SomeService', ['aMethod']);

    await TestBed.configureTestingModule({
      declarations: [ MyComponent ],
      providers: [
        { provide: SomeService, useValue: spy }
      ]
    })
    .compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    compiled = fixture.nativeElement;
  });

  it('should show another mock', () => {
    // I do all of these assignments at the end of the beforeEach
    // but I also declare the variables inside of the describe
    const mock: jasmine.SpyObj<SomeService> = TestBed.inject(SomeService) as jasmine.SpyObj<SomeService>;;
    mock.aMethod.and.returnValue('MOCKED VALUE');
   
   // I would either do this here or in the beforeEach, but not both
   // Each time you createComponent it can take a little bit of time
   // IF you decide to do this in each test, suggest creating a function
   // as there can get to be a list of items to assign
   // fixture = TestBed.createComponent(MyComponent);
   // compiled = fixture.nativeElement;

    fixture.detectChanges(); // first detectChanges will call onInit

    expect(compiled.querySelector('p').innerHTML).toContain('MOCKED VALUE');
  });
});

Is there a way to force the change detection, regardless of input change?

Why yes, however I tend to avoid this as I like to control when change detection runs. Plus, having to include a call to await whenStable can wait through your timers/debounce for the actual amount of time instead of moving through fake time.

What if I want to call detectChanges() on beforeEach()? Then I can't setup mocks inside specific tests?

I mean, yes. This would limit what you can change before the constructor and the onInit. There is a place and time for an inner describe with its own beforeEach for when your user is going to take different actions, but the data setup is the same.

Is it a good practice to call createComponent inside each specific test (it) instead?

If a lot of assignments happen at constructor time and you need to control the values, then yes. This has to do with how you write the component. When this is necessary I will create a function that does all of the TestBed.createComponent and dependency assignments for me. I honestly use functions in my test code a lot to help with readability, as they can get very messy and then hard to maintain.

Reasons:
  • Blacklisted phrase (1): Is there a way
  • RegEx Blacklisted phrase (1): I want
  • RegEx Blacklisted phrase (0.5): Why is that
  • Long answer (-1):
  • Has code block (-0.5):
Posted by: Wesley Trantham