0

亲爱的 stackoverflow 社区,您好,
我们在项目中执行单元测试时遇到了不稳定。我们将不胜感激有关该问题的任何帮助或意见。

问题

do-something.effects.spec.ts并且do-something.selectors.spec.ts单元测试在多次执行时似乎会遇到夹具数据冲突。

代码

do-something.selectors.spec.fixture.ts

import { DEFAULT_ROUTER_FEATURENAME, RouterReducerState } from '@ngrx/router-store';

import { IDoSomethingState } from '../../models/do-something-state';
import { Feature } from '../../models/event';
import { Foo } from '../../models/foo';
import * as fromDoSomething from './do-something.reducer';

interface ITestData {
  readonly foo: Foo;
}

const testData: ITestData = {
  foo: new Foo(
    'second'
  )
};


export class DoSomethingSelectorsFixture {
  state: IDoSomethingState;

  getStateForFeature() {
    const mockRouterUrl = '/tab2/restore';
    return {
      [DEFAULT_ROUTER_FEATURENAME]: {
        state: {
          url: mockRouterUrl
        }
      } as RouterReducerState,
      [Feature.DoSomething]: {
        ...fromDoSomething.initialState,
        [mockRouterUrl]: this.state
      }
    };
  }

  setupDoSomethingState() {
    this.state = {
      foo: testData.foo,
      bar: undefined
    } as IDoSomethingState;
  }
}

do-something.selectors.spec.ts

import { Foo } from '../../models/foo';
import { selectFoo } from './do-something.selectors';
import { DoSomethingSelectorsFixture } from './do-something.selectors.spec.fixture';

describe('DoSomethingFoo Selectors', () => {
  describe('select foo', () => {
    it('should return foo', () => {

      // Arrange
      const fixture = new DoSomethingSelectorsFixture();
      fixture.setupDoSomethingState();

      // Act
      const result = selectFoo(fixture.getStateForFeature());

      // Assert
      expect(result).not.toBeNull();
      expect(result).not.toBeUndefined();
      expect(result).toEqual(fixture.state.foo as Foo);
    });
  });
});

do-something.effects.spec.fixture.ts

import { Bar, IBar } from '../../models/bar';
import { Foo } from '../../models/foo';

interface Fixture {
  readonly foo: Foo,
  readonly bar: IBar
}

export const fixture: Fixture = {
  foo: new Foo(
    'first'
  ),
  bar: new Bar(
    123
  )
};

do-something.effects.spec.ts

import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { DEFAULT_ROUTER_FEATURENAME } from '@ngrx/router-store';
import { TypedAction } from '@ngrx/store/src/models';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { Observable, of } from 'rxjs';

import { DoSomethingApiService } from '../../api/do-something-api.service';
import { Feature } from '../../models/event';
import {
  loadBar,
  loadBarSuccess,
} from './do-something.actions';
import { DoSomethingEffects } from './do-something.effects';
import { fixture } from './do-something.effects.spec.fixture';
import {
  selectFoo
} from './do-something.selectors';

describe('DoSomethingFooEffects', () => {
  let actions$: Observable<TypedAction<string>>;
  let effects: DoSomethingEffects;
  let store: MockStore;
  let apiService: DoSomethingApiService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([
          {
            path: 'tab2',
            children: []
          }
        ])
      ],
      providers: [
        DoSomethingEffects,
        provideMockActions(() => actions$),
        provideMockStore({
          initialState: {
            [DEFAULT_ROUTER_FEATURENAME]: {
              state: {
                url: '/myUrl',
              },
            },
            [Feature.DoSomething]: {
              ['/myUrl']: undefined
            }
          },
        }),
        {
          provide: DoSomethingApiService,
          useFactory: (): Partial<DoSomethingApiService> => ({
            getBarOfFoo: () => of(undefined)
          }),
        }
      ],
    });

    effects = TestBed.inject(DoSomethingEffects);
    store = TestBed.inject(MockStore);
    apiService = TestBed.inject(DoSomethingApiService);
  });

  describe('loadBarOfFoo', () => {
    it('should return loadBarOfFooSuccess on success', async () => {
      // Arrange
      store.overrideSelector(selectFoo, fixture.foo);
      spyOn(
        apiService,
        'getBarOfFoo'
      ).and.returnValue(of(fixture.bar));
      actions$ = of(
        loadBar({ url: 'test' })
      );

      // Act
      const output =
        await effects.loadBarOfFoo$.toPromise();

      // Assert
      expect(output).toEqual(
        loadBarSuccess({
          url: '/myUrl',
          bar: fixture.bar,
        })
      );
    });
  });
});

Karma 单元测试日志输出

Chrome Headless 98.0.4758.109 (Mac OS 10.15.7): Executed 0 of 2 SUCCESS (0 
Chrome Headless 98.0.4758.109 (Mac OS 10.15.7): Executed 1 of 2 SUCCESS (0 
Chrome Headless 98.0.4758.109 (Mac OS 10.15.7) DoSomethingFoo Selectors select foo should return foo FAILED
        Error: Expected $.name = 'first' to equal 'second'.
            at <Jasmine>
            at UserContext.<anonymous> (src/app/features/do-something/do-something.selectors.spec.ts:19:22)
            at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:400:1)
            at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:301:43)
Chrome Headless 98.0.4758.109 (Mac OS 10.15.7): Executed 2 of 2 (1 FAILED) (0 secs / 0.055 secs)
Chrome Headless 98.0.4758.109 (Mac OS 10.15.7) DoSomethingFoo Selectors select foo should return foo FAILED
        Error: Expected $.name = 'first' to equal 'second'.
            at <Jasmine>
            at UserContext.<anonymous> (src/app/features/do-something/do-something.selectors.spec.ts:19:22)
            at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:400:1)
            at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testi
Chrome Headless 98.0.4758.109 (Mac OS 10.15.7): Executed 2 of 2 (1 FAILED) (0.072 secs / 0.055 secs)
TOTAL: 1 FAILED, 1 SUCCESS
TOTAL: 1 FAILED, 1 SUCCESS

存储库

https://github.com/Olddude/flaky-selector-tests

技术

4

0 回答 0