【问题标题】:Mocking ngrx/store模拟ngrx/store
【发布时间】:2016-11-29 04:31:52
【问题描述】:

这是关于 Angular 2 官方版本的。我知道单元测试在 beta、RC 和正式版本之间发生了巨大变化。

当 @ngrx/store 用作构造函数中的参数时,在单元测试中模拟 @ngrx/store 的好方法是什么?这不像模拟一个服务那么简单。

例如,如果我想模拟一个服务,那么我可以这样做:

let serviceStub = { }; // not a true mocked service, just a stub, right?

let de: DebugElement;
let el: HTMLElement;
let nativeEl: Element;
let comp: Typeahead;
let fixture: ComponentFixture<Typeahead>;

describe('Component:Typeahead', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [...],
            declarations: [Typeahead],
            providers: [
                {provide: TypeaheadService, useValue: serviceStub} // provides the service that is being "mocked"
            ]
        }).compileComponents();

        fixture = TestBed.createComponent(Typeahead);
        nativeEl = fixture.nativeElement;
        comp = fixture.componentInstance;
        de = fixture.debugElement;
    });
});

这行得通。

但是,对于ngrx/store,它不会(如果您用 Store in 代替 TypeaheadService)。我认为您必须编写一个扩展 Store 的模拟类,然后将其提供给正在测试的组件,但我不确定为什么会这样(如果是这样的话)。

我只是对如何在我的单元测试中模拟 ngrx/store 感到困惑,并且在他们的网站或 github 上找不到任何文档。也许我忽略了它。

【问题讨论】:

  • 你想测试你的减速器还是真正测试一个组件?
  • 我已经测试了减速器。我想测试组件。这不是最佳做法吗?
  • 我想会的。只是在我目前正在进行的一个项目中,我测试了我的减速器,并且还进行了 E2E 测试。问题是,到目前为止,我并不觉得有必要测试我的组件,因为它们不能处理那么多逻辑。甚至是“智能组件”。我发现使用let,您可以创建“生成视图”或“计算视图”,它允许您将大量逻辑移出框架。它可能并不完美,也许以后我会觉得有必要测试组件:)。

标签: unit-testing angular jasmine ngrx


【解决方案1】:

感谢您发布问题并提出潜在的解决方案!

我模拟它的方式是,使用实际操作来设置初始状态,即每次测试之前的模拟状态。这是一个例子

beforeEach(inject([Store], (store: Store<ApplicationState>) => {
const someFakeState = {
    counter: 9,
    counterFilter: 'A_FAKE_COUNTER_FILTER'
};

store.dispatch(new myActionToSetSomeData(someFakeState));
}));

在您的 it() 块内,您现在应该能够检查该组件是否显示计数为 9 并通过 'A_FAKE_COUNTER_FILTER' 进行过滤。

您当然可以在 it 块中设置状态,而不是 beforeEach,只要它在组件被实例化之前。

【讨论】:

  • 哇,这就是最小的设置!你在 Angular 的官方版本中使用这个吗?
  • 您好,非常抱歉 - 有一段时间没有登录了。是的,这就是我在官方 Angular 2 设置上的工作方式。 (也适用于 Angular 4,因为它实际上更多地与 ngrx 相关,而不是 Angular)
【解决方案2】:

您可以使用forRoot (>= v4) 或provideStore (

1 - 导入它:

import { StoreModule } from '@ngrx/store';

2 - 创建模拟数据:

/*
* Mock data
*/
const PAINTS = [];

3 - 在你的测试中导入它:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    imports: [ StoreModule.forRoot(PAINTS) ]
  })
}))

在以前的版本(v4 之前)中,您应该使用provideStore(PAINTS) 而不是forRoot(PAINTS)。查看更新日志here

【讨论】:

  • 这不完全是 嘲弄 坚果,我投票赞成简单。
  • 您是否也设法在此配置中测试了效果?我已经发布了 question 描述我这样做的尝试。我在解决效果类的依赖项时遇到了问题。
【解决方案3】:

是的,您必须模拟 ngrx/store,但不仅限于 Store。 Store 需要三个参数;一个 Observable 类型,两个 Observer 类型,它是一个接口。所以,我尝试了两件事。将空值传递给 StoreMock super() 构造函数,但这在我的断言中失败了。我的另一个解决方案是使用模拟类(在本例中为 Observable)实现 Observer 接口。这样我可以将定义的值传递给超级 StoreMock 构造函数。

这只是一个说明性示例。 ObservableMock 实际上并没有模拟我试图在我的应用程序中测试的任何功能。它充当推动者,以便可以将 Store 作为提供者注入到我正在尝试测试的组件中。

由于 Observer 是一个接口,你必须在 mock 中实现它的函数声明:nexterrorcomplete

class ObservableMock implements Observer<any> {
  closed?: boolean = false; // inherited from Observer
  nextVal: any = ''; // variable I made up

  constructor() {}

  next = (value: any): void => { this.nextVal = value; };
  error = (err: any): void => { console.error(err); };
  complete = (): void => { this.closed = true; }
}

let actionReducer$: ObservableMock = new ObservableMock();
let action$: ObservableMock = new ObservableMock();
let obs$: Observable<any> = new Observable<any>();

class StoreMock extends Store<any> {
  constructor() {
    super(action$, actionReducer$, obs$);
  }
}

现在您可以在组件的测试模块中添加 Store 作为提供程序。

describe('Component:Typeahead', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [...],
            declarations: [Typeahead],
            providers: [
                {provide: Store, useClass: StoreMock} // NOTICE useClass instead of useValue
            ]
        }).compileComponents();
    });
});

我相信还有其他方法可以做到这一点。因此,如果有人有任何其他答案,请发布!

【讨论】:

    猜你喜欢
    • 2017-08-30
    • 2017-10-08
    • 2017-09-08
    • 1970-01-01
    • 2018-03-30
    • 2020-07-14
    • 1970-01-01
    • 2018-01-30
    • 1970-01-01
    相关资源
    最近更新 更多