1

Here is my oninit class, which I am not supposed to change.

  ngOnInit(): void {
        this.setCustomizedValues();
        this.sub = PubSub.subscribe('highlightEntity', (subId, entityIdentifier: string) => {
          document.querySelector(entityIdentifier).classList.add('highlight');
    
          setTimeout(() => {
            document.querySelector(entityIdentifier).classList.remove('highlight');
          }, 2000);
    
        });
      }

and here is minimal test setup

describe('aComponent', () => {

    
  let httpCallerServiceStub = jasmine.createSpyObj('httpCallerServiceStub', ['get']);

  let fixture: ComponentFixture<aComponent>;
  let componentInstance: aComponent;
  localStorage.clear();
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [aComponent],
      providers: [
        {
          provide: HttpCallerService,
          useValue: httpCallerServiceStub
        }],
      imports: [CommonModule, CmcSpinnerModule, NgbModule],
    }).compileComponents();

    fixture = TestBed.createComponent(aComponent);
    componentInstance = fixture.componentRef.instance;
    fixture.autoDetectChanges();
  });

 describe('ngOnInit method', () => {
    beforeEach(() => {
      fixture.componentInstance.customized = {};
    });

    it('should initialize with minimum input', () => {
      // Triggers ngOnInit
      fixture.detectChanges();
      expect(fixture.componentInstance.customized).toEqual({});
    });
  });

The issue is the tests are failing randomly with the error that classlist not found of null

"message": "An error was thrown in afterAll\nTypeError: Cannot read property 'classList' of null\n    at <Jasmine>\n 
   at webpack:///components/path/aComponent.ts:35:53

this line no 35 refers to this lines in oninit function

   setTimeout(() => {
                document.querySelector(entityIdentifier).classList.remove('highlight');
              }, 2000);

So few questions here

  1. I have tried putting tick(2000) and fake asysnc in a simple test of ngonit but still it is giving me the same random errors (as suggested by this answer )

  2. Is my testbed configuration is missing something? because even when I comment out the oninit test and just check some random function even without fixture.detectChanges(), the same random error is happening.

  3. if I run tests using fdescibe, all the tests get passed every time. This issue only happens when I removed f from fdescribe and run all the tests of other components as well.

Please guide me, I am stuck here since last 4 days.

  • 1
    Judging by the fact that `fdescribe` is working, I have a feeling that the tests are failing because of some component, that uses `aComponent`. If this is a case, try mocking `aComponent` another unit test. – mat.hudak Oct 08 '20 at 08:08
  • ok sure , I will do that, so another component which use aComponent dont fail when there are no tests in aComponent specs – Talk is Cheap Show me Code Oct 08 '20 at 08:39
  • `aComponent` has some logic in `ngOnInit` and it's not correctly instantiated within the parent component's unit test. Try providing a mock for it, or use `ng-mocks` library. – mat.hudak Oct 08 '20 at 08:55
  • yes I just found out that there is one top level parent component which uses both of the component, ie the one for whom I am writing the test cases and second the one for which tests are failing – Talk is Cheap Show me Code Oct 08 '20 at 09:01
  • hey @dallows : I have figure out that another component which was getting affected by this aComponent and added needed tick(2000) now that all test cases failing issue is sovled, a new issue is debug.js:21 Error: 1 timer(s) still in the queue. where ever i have used tick(2000) in another component. (that too happening randomly ) – Talk is Cheap Show me Code Oct 08 '20 at 10:53

1 Answers1

2

To proceed further with the discussion from the comments:

I would suggest to use mocks instead of actually trying to make aComponent work in other unit tests. If it has its own dependencies it would throw such error every time you extend it with some other dependencies or init code. And if you don't plan to test that functionality within its parent component then the mock should do it.

And at the same time you'll get rid of any ticks and so on...

@Component({
  selector: 'a-component', //make sure the selector is same as in the real component
  template: '<p>Mock A Component</p>'
})
class MockAComponent {}

beforeEach(() => {
  TestBed.configureTestingModule({
      declarations: [
        ParentComponent,
        MockAComponent ,
      ],
      // ...
  });
});

Or a simple solution is to install ng-mocks and then you don't need to create it separately.

beforeEach(() => {
  TestBed.configureTestingModule({
      declarations: [
        ParentComponent,
        MockComponent(AComponent),
      ],
      // ...
  });
});

More about this is here: Mocking Child Components - Angular 2

mat.hudak
  • 2,803
  • 2
  • 24
  • 39