.detectChanges() not working within Angular test

It turns out this is due to using ChangeDetectionStrategy.OnPush in the component. Using OnPush only allows you to call .detectChanges() one time, so subsequent calls will fail to do anything. I'm not familiar enough with Angular to fully understand why.

I was able to produce the required behaviour by overriding the ChangeDetectionStrategy in my TestBed configuration.

TestBed.configureTestingModule({
    imports: [],
    declarations: [TestComponent],
    providers: []
  })
    .overrideComponent(TestComponent, {
      set: { changeDetection: ChangeDetectionStrategy.Default }
    })
    .compileComponents();

Still running on this to this day...

I personally like the overriding ChangeDetectionStrategy solution as it's a one time thing on TestBed setup, but I understand this kind of intrusive solution isn't ideal.

TestBed.configureTestingModule({
    imports: [],
    declarations: [TestComponent],
    providers: []
})
.overrideComponent(TestComponent, {
    set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();

There's the "ChangeDetectorRef" solution which I've seen being used on the Component class itself with "changeDetector.markForCheck()" and that is not a good way as your component should not have to adapt to testing, but you can still use this solution, without messing with the actual component, by calling instead of the normal "detectChanges()", as presented here

const cdr = debugEl.injector.get<ChangeDetectorRef>(ChangeDetectorRef as any);
cdr.detectChanges();

And finally there's the simplest solution, at least in my head, and which, curiously, I haven't found any mentions to it. So, you already probably know you can (or end up having to) create a host component to wrap the one you're testing, a lot of blogs, for example, out there showcase the usage of a @ViewChild(ComponentUnderTestComponent) approach which would be perfect if jasmine could actually perceive a change in the child component, but, as it looks like, it doesn't and we are stuck with the normal intuitive approach of just listing the inputs in the host and binding them directly in the template of the testing component, like this:

@Component({
    template: `<component-tag [(ngModel)]="val" [someProperty]="flag"></component-tag>`
})
class HostComponent {
    val: number;
    flag: boolean = false;
}

with that, now you can actually change the value of HostComponent.someProperty and then call detectChanges() and jasmine will perfectly do what it's supposed to and update the DOM with the change:

fixture.componentInstance.readonly = true;
fixture.detectChanges();

Now, if your component goes ahead and have dozens of input attributes, then I guess this isn't really viable, but anyway, I thought I'd throw it out there, enjoy