如何在调度NgRx单元测试后检测组件中的存储更改

huangapple go评论69阅读模式
英文:

How to detect store change in component after dispatch NgRx Unit testing

问题

在将状态从'light'更改为'dark'后,它不会在组件中更改。它会保持状态为'light',即使我已经分派了一个将其更改为'dark'的操作。有没有办法在组件中检测到它?

以下是单元测试文件

app.component.spec.ts

describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let compiled: HTMLElement;
  let store: MockStore;

  const initialState = {
    theme: fromTheme.initialState,
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent],
      imports: [AppRoutingModule],
      providers: [provideMockStore({ initialState })],
    }).compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
    compiled = fixture.debugElement.nativeElement;
    store = TestBed.get<MockStore>(MockStore);
  });

  it('should create the app', () => {
    expect(component).toBeTruthy();
  });

  it('should have a class with theme', () => {
    expect(compiled.classList.contains('light')).toBeTrue();

    store.dispatch(fromTheme.changeTheme({ payload: 'dark' }));

    fixture.detectChanges();

    expect(compiled.classList.contains('dark')).toBeTrue();
  });
});

以下是组件文件

app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  host: {
    '[class.light]': 'theme === "light"',
    '[class.dark]': 'theme === "dark"',
  },
})
export class AppComponent implements OnInit, OnDestroy {
  theme: ThemeType = 'light';

  themeSubscription: Subscription | null = null;

  constructor(
    private store: Store<AppStore>,
  ) {}

  ngOnInit() {
    this.themeSubscription = this.store.select(selectTheme).subscribe((themeState) => {
      this.theme = themeState.theme;

      if (!themeState.isChanged) {
        const preferredTheme = getPreferredTheme();

        this.store.dispatch(changeTheme({ payload: preferredTheme }));
      }
    });
  }

  ngOnDestroy() {
    this.themeSubscription?.unsubscribe();
  }
}
英文:

After changing the the state from 'light' to 'dark' it's not gonna change it in component. It will keep the state as 'light' even though I have dispatched an action which is gonna change it to dark.
Is there a way to detect it in component?

Here is the unit test file

app.component.spec.ts

describe(&#39;AppComponent&#39;, () =&gt; {
  let component: AppComponent;
  let fixture: ComponentFixture&lt;AppComponent&gt;;
  let compiled: HTMLElement;
  let store: MockStore;

  const initialState = {
    theme: fromTheme.initialState,
  };

  beforeEach(() =&gt; {
    TestBed.configureTestingModule({
      declarations: [AppComponent],
      imports: [AppRoutingModule],
      providers: [provideMockStore({ initialState })],
    }).compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
    compiled = fixture.debugElement.nativeElement;
    store = TestBed.get&lt;MockStore&gt;(MockStore);
  });

  it(&#39;should create the app&#39;, () =&gt; {
    expect(component).toBeTruthy();
  });

  it(&#39;should have a class with theme&#39;, () =&gt; {
    expect(compiled.classList.contains(&#39;light&#39;)).toBeTrue();

    store.dispatch(fromTheme.changeTheme({ payload: &#39;dark&#39; }));

    fixture.detectChanges();

    expect(compiled.classList.contains(&#39;dark&#39;)).toBeTrue();
  });
});

Here is the component file

app.component.ts

@Component({
  selector: &#39;app-root&#39;,
  templateUrl: &#39;./app.component.html&#39;,
  styleUrls: [&#39;./app.component.scss&#39;],
  host: {
    &#39;[class.light]&#39;: &#39;theme === &quot;light&quot;&#39;,
    &#39;[class.dark]&#39;: &#39;theme === &quot;dark&quot;&#39;,
  },
})
export class AppComponent implements OnInit, OnDestroy {
  theme: ThemeType = &#39;light&#39;;

  themeSubscription: Subscription | null = null;

  constructor(
    private store: Store&lt;AppStore&gt;,
  ) {}

  ngOnInit() {
    this.themeSubscription = this.store.select(selectTheme).subscribe((themeState) =&gt; {
      this.theme = themeState.theme;

      if (!themeState.isChanged) {
        const preferredTheme = getPreferredTheme();

        this.store.dispatch(changeTheme({ payload: preferredTheme }));
      }
    });
  }

  ngOnDestroy() {
    this.themeSubscription?.unsubscribe();
  }
}

答案1

得分: 1

provideMockStore 不运行 reducers/effects。
因为 reducer 是一个纯函数,所以通过 reducer 方法进行测试更容易。这样做也可以加快测试速度,因为你不需要 TestBed/Setup/Component。

示例请参见 https://timdeschryver.dev/blog/testing-an-ngrx-project#reducers

如果你需要测试组件,那么你需要手动设置状态/覆盖选择器,参见 https://ngrx.io/guide/store/testing#using-mock-selectors 以获取示例。

或者,不使用 mockstore,依赖于真实的实现。

英文:

provideMockStore doesn't run the reducers/effects.
Because a reducer is a pure function it's also easier to just test the reducer via the reducer method. This makes your tests also faster because you don't need the TestBed/Setup/Component.

For an example see https://timdeschryver.dev/blog/testing-an-ngrx-project#reducers

If you need to test the component then you need to set the state manually/override the selector, see https://ngrx.io/guide/store/testing#using-mock-selectors for an example.

Or, don't use the mockstore and rely on the real impementation.

答案2

得分: 0

以下是翻译好的代码部分:

不需要提供模拟存储,而可以像这样提供真实存储:

let store: Store<AppStore>;

beforeEach(() => {
  TestBed.configureTestingModule({
    declarations: [AppComponent],
    imports: [AppRoutingModule, StoreModule.forRoot(appReducer)],
  });

  fixture = TestBed.createComponent(AppComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();
  compiled = fixture.debugElement.nativeElement;
  store = TestBed.get<Store>(Store);
});

然后组件将监听此存储中的更改,并且调度将起作用。

测试示例:

it('应该具有包含主题的类', () => {
  store.select(fromTheme.selectTheme)
    .subscribe((themeState) => {
      expect(compiled.classList.contains(themeState.theme)).toBeTrue();
    });
});

---

it('应该能够调度changeTheme', () => {
  const newTheme = 'dark';

  store.dispatch(fromTheme.changeTheme({ payload: newTheme }));

  expect(compiled.classList.contains(newTheme)).toBeTrue();
});

请注意,上面的翻译是给出的代码部分的中文翻译,不包含其他内容。

英文:

There was no need to provide a mock store, instead of that, we can provide the real store like this

let store: Store&lt;AppStore&gt;;

beforeEach(() =&gt; {
  TestBed.configureTestingModule({
    declarations: [AppComponent],
    imports: [AppRoutingModule, StoreModule.forRoot(appReducer)],
  });

  fixture = TestBed.createComponent(AppComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();
  compiled = fixture.debugElement.nativeElement;
  store = TestBed.get&lt;Store&gt;(Store);
});

Then the component will listen for changes in this store, and the dispatch will work.

Test Examples:

it(&#39;should have a class with theme&#39;, () =&gt; {
  store.select(fromTheme.selectTheme)
    .subscribe((themeState) =&gt; {
      expect(compiled.classList.contains(themeState.theme)).toBeTrue();
    });
});

it(&#39;should be able to dispatch the changeTheme&#39;, () =&gt; {
  const newTheme = &#39;dark&#39;;

  store.dispatch(fromTheme.changeTheme({ payload: newTheme }));

  expect(compiled.classList.contains(newTheme)).toBeTrue();
});

huangapple
  • 本文由 发表于 2023年6月19日 04:54:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76502496.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定