【问题标题】:property is not declared configurable (Error: <spyOnProperty>)属性未声明为可配置(错误:<spyOnProperty>)
【发布时间】:2026-01-14 20:25:18
【问题描述】:

给定一个MenuComponent

@Component({
  selector: 'cg-menu',
  templateUrl: './menu.component.html',
  styleUrls: [ './menu.component.scss' ]
})
export class MenuComponent implements OnInit {
  menu: MenuItem[];

  isLoggedIn$ = this.authStateService.isLoggedIn.pipe(untilDestroyed(this));

  constructor(private readonly authStateService: AuthStateService,
              private readonly router: Router) {
  }

  ngOnInit() {
    this.authStateService
      .state
      .pipe(
        untilDestroyed(this),
        ...some logic...
      .subscribe();
  }

  ...some logic...
}

还有一个AuthStateService

@Injectable({ providedIn: 'root' })
export class AuthStateService {
  private readonly state$: Observable<AuthState | null>;
  private readonly isLoggedIn$: Observable<boolean | null>;

  constructor(private readonly sessionManager: SessionManagerService) {

    this.state$ = this.stateTrigger$
      .pipe(
        ...some logic...
        shareReplay(1));

    this.isLoggedIn$ = this.stateTrigger$
      .pipe(
        map(state => state !== null),
        shareReplay(1));
  }

  get state(): Observable<AuthState> {
    return this.state$;
  }

  get isLoggedIn(): Observable<boolean> {
    return this.isLoggedIn$;
  }

  ...some logic...
}

当我尝试运行此代码时,Karma 触发了一个错误。

describe('MenuComponent', () => {
  let component: MenuComponent;
  let fixture: ComponentFixture<MenuComponent>;

  let authStateServiceSpy;

  beforeEach(async(() => {
    authStateServiceSpy = jasmine.createSpyObj(
      'AuthStateService',
      {},
      {
        state: of(null),
        isLoggedIn: of(false)
      });

    TestBed.configureTestingModule({
      declarations: [ MenuComponent ],
      imports: [ RouterTestingModule ],
      providers: [
        { provide: AuthStateService, useValue: authStateServiceSpy }
      ],
      schemas: [ NO_ERRORS_SCHEMA ]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(MenuComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should generate correct menu (expert)', () => {
    // The problem is here
    spyOnProperty(authStateServiceSpy, 'state', 'get').and.callFake(() => of(stateMock));

    expect(component.menu).toEqual([
      ...something...
    ]);
  });
});

错误是这样的。

Error: <spyOnProperty> : state is not declared configurable
    Usage: spyOnProperty(<object>, <propName>, [accessType])
    Error: <spyOnProperty> : state is not declared configurable
    Usage: spyOnProperty(<object>, <propName>, [accessType])
        at <Jasmine>
        at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/src/app/modules/structure/components/menu/menu.component.spec.ts:55:5)
        at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:364:1)
        at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:292:1)
        at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:363:1)
        at Zone.run (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:123:1)
        at runInTestZone (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:545:1)
        at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:560:1)

知道我做错了什么吗? 感谢您的帮助。

【问题讨论】:

    标签: angular typescript unit-testing jasmine karma-runner


    【解决方案1】:

    我猜这是因为AuthStateServicestate 是类中的getter,但你的模拟 将其声明为常规属性。您也应该将其声明为 getter

    这个问题似乎与将createSpyObjspyOnProperty 结合使用有关,因为它不是您要监视的真正的getter

    查看您的代码,似乎没有理由使用createSpyObj,所以这可能会奏效:

        // ... 
    
        authStateServiceMock = {
            get state() { return of(null) },
            get isLoggedIn() { return of(false) },
        };
    
        // ...
    
        spyOnProperty(authStateServiceMock, 'state', 'get').and.callFake(() => of(stateMock));
        
        expect(component.menu).toEqual([
            ...something...
        ]);
    

    【讨论】:

    • 知道怎么做吗?我尝试了很多东西,但没有一个有效。
    • 仍然无法正常工作。这样不会触发任何错误,但根本不会改变状态。
    • @simonepachera 我猜你的 component.menu 在访问 getter 之前被初始化(例如在组件初始化期间)。模拟需要在此之前进行设置。
    • 行不通。仍然收到Error: &lt;spyOnProperty&gt; : &lt;functionName&gt; is not declared configurable