はじめに
Angularのテストをjasmineで書いています。
テスト対象のComponentをextends
していますが、拡張元のComponentのプロパティがreadOnly
で変更できず詰みました。
トリッキーな方法かもしれませんが、以下のObject.defineProperty()
を使用する方法でreadOnlyのプロパティを変更できたので、共有します。
やりたいこと
Angularのテストをjasmineで書いた場合、拡張元のComponentの読み込み専用プロパティの値を変更したいです。
ちなみに、このプロパティは本来の実装で変更の必要がないものです。しかし、分岐の条件として使われているため、テストでは変更する必要がありました。
やったこと
最初、実際のコードと同様に、拡張元のBaseComponent
でテスト対象のAppComponent
を拡張しようとしました。これは、方法がわからず調査に時間がかかりそうということがわかりました。やり方をご存知の方がいたら、教えてくださいー!
結果、readOnlyのプロパティを書き変える方法としてObject.defineProperty()
を使うことにしました。
テスト対象のコード
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export abstract class BaseComponent {
readonly str: string = 'base';
}
export class AppComponent extends BaseComponent {
constructor() {
super();
}
}
テストコード
読み込み専用のプロパティに単純に値を代入しても、Cannot assign to 'str' because it is a read-only property.
エラーになります。
ですが、Object.defineProperty()
を使うと、エラーにならず値を書き換えることができます。
import { TestBed } from '@angular/core/testing';
import { ComponentFixture } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent tests', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
imports: [],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should succeed', () => expect(component).toBeDefined(true));
it('拡張元のプロパティを書き換える', () => {
// 変更前の値確認
expect(component.str).toEqual('base');
// NG例: strが読み込み専用なので、実行できない。Cannot assign to 'str' because it is a read-only property.
// component.str = "change";
// OK例
Object.defineProperty(component, 'str', { value: 'change' });
// 結果確認
expect(component.str).toEqual('change');
});
});
テスト結果
おわりに
今回は、readOnlyの値を強制的に書き換える方法として、Object.defineProperty()
を紹介しました。
よい方法なのか、確証がないので使う場合は自己判断でお願いします!
ちなみに、この方法は、拡張元でなくテスト対象のComponentの読み込み専用プロパティにも使用できます。あればsetterを使って実装するのが、本来のコードの実行に近くてふさわしいと思います。
また、テストでない実際の実装にも使えるはずですが、使おうとした場合設計がおかしいと思います。その場合、設計を見直すことをオススメします。