我正在尝试为 Angular 中的服务编写单元测试。我想模拟 ngrx 的 store.select 函数,这样我就可以测试服务如何对存储选择器返回的不同值做出反应。我希望能够单独模拟每个选择器。
我的主要问题是如何模拟参数化选择器。
我之前使用过映射到 select 函数的 BehaviourSubject,但这不允许您为不同的选择器返回不同的值。它不可读,因为你正在嘲笑哪个选择器并不明显。
选项1:使用主题的模拟存储:无法知道主题对应哪个选择器,无法为不同的选择器返回不同的值。
// 服务.spec.ts
const selectSubject = new BehaviourSubject(null);
class MockStore {
select = () => selectSubject;
}
Run Code Online (Sandbox Code Playgroud)
选项2:使用开关模拟存储:适用于不同的选择器,但当选择器有参数时无法使其工作。
// 服务.spec.ts
// This works well but how can I make it work with selectors with parameters??
const firstSubject = new BehaviourSubject(null);
const secondSubject = new BehaviourSubject(null);
class MockStore {
select = (selector) => {
switch (selector): {
case FirstSelector: {
return firstSubject;
}
case SecondSelector: {
return secondSubject;
} …Run Code Online (Sandbox Code Playgroud) 我正在Angular 5中实现一个反应形式,我需要在两个事件中触发验证,当字段模糊时以及提交表单时.
我已使用updateOn属性将其设置为"模糊",但如果您专注于某个字段并按Enter键,则不会触发模糊事件,并且不会更新该字段的值,除非我单击远离领域.
据我所知,没有办法将updateOn设置为模糊和提交.
有没有办法实现这个目标?
我有一些 Angular 6 表单,我专注于第一个可用字段。它们包含一个提交按钮,当我按 Enter 时,表单会自动提交并触发 ngSubmit 事件。
有一种特殊的形式只包含一个广播组,我不关注任何领域。在这种情况下,不会触发 ngSubmit。
如果我单击单选按钮然后按 Enter 键,表单将正确提交。
即使表单中没有任何字段都处于焦点状态,是否有任何方法可以使表单在输入时提交?我应该以某种方式关注表单本身吗?
形式:
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<mat-radio-group [formControl]="control" name="test">
<mat-radio-button *ngFor="let option of options" [value]="option.key">
{{option.value}}
</mat-radio-button>
</mat-radio-group>
<button>Submit</button>
</form>
Run Code Online (Sandbox Code Playgroud)
Stackblitz: https://stackblitz.com/edit/angular-arcwjv? file=src/app/app.component.html
我有一个返回由ngrx选择器公开的值的服务。组件定义此服务以获取数据。我正在使用服务的模拟为组件编写单元测试,并且我需要模拟服务为每个单元测试返回不同的值。我该如何实现?
零件
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
providers: [TestService],
})
export class TestComponent {
test$ = this.testService.test$;
test: number;
constructor(private service: TestService) {
service.test$.subscribe(test => this.test = test);
}
}
Run Code Online (Sandbox Code Playgroud)
服务
export class TestService {
test$ = this.store.select(getTestValueFromStore);
constructor(private store: Store<any>){}
}
Run Code Online (Sandbox Code Playgroud)
尝试1(重置服务的值):不起作用
class MockTestService {
**test$ = of(10);**
}
describe('TestComponent', () => {
let testService: TestService;
beforeEach((() => {
// Define component service
TestBed.overrideComponent(
TestComponent,
{ set: { providers: [{ provide: TestService, useClass: MockTestService }] } }
);
TestBed.configureTestingModule({ …Run Code Online (Sandbox Code Playgroud) 我的应用程序正在使用 Angular 和 ngrx 商店。我在商店中使用选择器来获取组件构造函数中应用程序状态的用户名属性:
测试组件.ts
export class TestComponent {
username$: Observable<string>;
constructor(private store: Store<fromRoot.AppState>) {
this.username$ = this.store.select(fromRegistration.getUsername);
}
testMethod() {
//I need the value of the username observable here as a string
}
}
Run Code Online (Sandbox Code Playgroud)
这工作正常。我需要在组件的模板中打印用户名,这是我使用异步管道完成的:
测试组件.html
<div>
{{username | async}}
</div>
Run Code Online (Sandbox Code Playgroud)
这也正常工作。
现在,我需要从 TestComponent 中的一个方法调用一个服务,该方法将用户名作为参数发送。我怎样才能做到这一点?我是否需要停止使用异步管道并订阅选择器,然后将 username 属性(然后将其声明为字符串)分配给 observable 的值?
注意:用户名 observable 只有 1 个值,一旦我得到 1 个值,我就可以停止观看它。
我知道通过使用异步管道组件会自动从用户名 observable 取消订阅,这什么时候发生,组件如何知道我不再需要观看它?
如果我不能使用异步管道,我应该如何以及何时取消订阅?
http请求成功后,我需要从ngrx效果中调度多个操作。
似乎有几种方法行得通,有些则行不通,我不明白有什么区别。
@Effect()
loadUser = this.actions
.pipe(
ofType(campaignActions.type.LOAD_CAMPAIGN),
map((action: userActions.LoadUserAction) => action.payload),
switchMap((payload: { id: number }) =>
this.s.getUser(payload.id)
.pipe(
switchMap((data) => {
return from([
new userActions.LoadUserSuccessAction(),
new userActions.SomethingElseSuccessAction()
]);
}),
catchError(() => of(new userActions.LoadUserFailureAction()))
)
)
);
Run Code Online (Sandbox Code Playgroud)
首先,我可以使用switchMap或mergeMap。我相信不同之处在于,如果多次触发此效果,那么如果我使用switchMap,而没有使用mergeMap,则所有正在进行的请求都将被取消。
在分派多个动作方面,以下所有工作:
以下内容不起作用:
前三个选项之间有什么区别?http请求之后,mergeMap和switchMap怎么样?为什么最后一个选项不起作用?
我有一个Angular 6应用程序,我懒于加载模块并通过路由器传递一些数据。进入模块后,我在共享服务中调用一个方法,并将此数据(配置资料)传递给该方法。
我需要在该模块被销毁时(实质上是在用户离开该路径时)调用一个方法,但是我只想调用一次,所以我想避免不得不观察不断变化的路线,因为这会很昂贵。原因是我需要重置共享服务配置。基本上,我具有应用程序的某些配置数据,并且需要为延迟加载的模块覆盖它,但是一旦用户不在模块中,就将其放回去。
我正在尝试在模块的OnDestroy挂钩上调用该函数,但未触发。
路由模块:
const appRoutes: Routes = [
{
path: 'lazy',
loadChildren: 'lazy.module#LazyModule',
data: {
test: 'data'
}
},
{
path: 'home',
loadChildren: 'home.module#HomeModule',
},
{
path: '**',
redirectTo: '/home'
}
]
Run Code Online (Sandbox Code Playgroud)
延迟加载的模块:
export class LazyModule implements OnDestroy {
constructor() {
console.warn('LazyModule launched!'); // I see this
}
ngOnDestroy() {
console.warn('destroyed'); // This is not triggered
}
}
Run Code Online (Sandbox Code Playgroud) 我在组件中定义了一个可观察的(result $),并通过异步管道将其显示在其模板上。可观察值是通过CombineLatest将其他2个可观察值(first $,second $)组合而成的。如果其中一个或两个可观察对象发出的时间过早(在我发现ngAfterContentInit之前),则所得的可观察对象将不会发出值。
组件:不起作用
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
result$: Observable<number>;
first$ = new Subject<number>();
second$ = new Subject<number>();
constructor() {}
ngOnInit(){
this.result$ = combineLatest(
this.first$,
this.second$
).pipe(
map(([first, second]) => {
// This is not printed to the console
console.log('combined obs emitted value');
return first + second;
})
);
console.log('first and second emit value');
this.first$.next(2);
this.second$.next(4);
}
ngAfterContentInit() {
console.log('ngAfterContentInit');
}
}
Run Code Online (Sandbox Code Playgroud)
执行顺序为:
1.第一和第二发射值
2.ngAfterContentInit
我在这里的假设是,在ngAfterViewInit中,模板已渲染,并且已进行预订。因为可观察对象在此之前发出一个值,所以不会通知组件。这仅意味着结果的可观察对象是冷的(因此,您需要在其发出值之前进行预订)。这两个可观察对象是对象,因此我认为对象是冷可观察对象。它是否正确?
如果我延迟了first $和second $的发射,则一切正常:Component:first …
我有一个在推送时使用更改检测的 Angular 组件。该组件有一个输入,当路由更新时该输入会被组件修改。如果我将输入分配给新引用并修改它,模板中的值不会更新。我认为只要分配一个新对象,就会检测到更改,但除非我在 ChangeDetectorRef 上调用 detectorChanges(),否则不会检测到。
@Component({
selector: 'app-nav-links',
templateUrl: './nav-links.component.html',
styleUrls: ['./nav-links.component.less'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NavLinksComponent implements OnInit {
@Input() links: Link[] = [];
private currentPath: string;
constructor(private router: Router,
private route: ActivatedRoute,
private cdr: ChangeDetectorRef) { }
ngOnInit() {
this.router.events.subscribe(() => {
this.updateActiveRoute();
});
}
private updateActiveRoute() {
this.currentPath = this.route.snapshot.children[0].routeConfig.path;
this.links = [...this.links].map(link => {
link.active = link.url === this.currentPath;
return link;
});
// Why is this required if I am already using the spread …Run Code Online (Sandbox Code Playgroud) angular ×9
ngrx ×4
rxjs ×3
forms ×2
unit-testing ×2
effects ×1
jasmine ×1
observable ×1
store ×1
subject ×1
validation ×1