0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptでreadOnlyの値を強制的に上書きする方法

Last updated at Posted at 2022-05-28

はじめに

Angularのテストをjasmineで書いています。
テスト対象のComponentをextendsしていますが、拡張元のComponentのプロパティがreadOnlyで変更できず詰みました。

トリッキーな方法かもしれませんが、以下のObject.defineProperty()を使用する方法でreadOnlyのプロパティを変更できたので、共有します。

やりたいこと

Angularのテストをjasmineで書いた場合、拡張元のComponentの読み込み専用プロパティの値を変更したいです。

ちなみに、このプロパティは本来の実装で変更の必要がないものです。しかし、分岐の条件として使われているため、テストでは変更する必要がありました。

やったこと

最初、実際のコードと同様に、拡張元のBaseComponentでテスト対象のAppComponentを拡張しようとしました。これは、方法がわからず調査に時間がかかりそうということがわかりました。やり方をご存知の方がいたら、教えてくださいー!

結果、readOnlyのプロパティを書き変える方法としてObject.defineProperty()を使うことにしました。

テスト対象のコード

app.component.ts
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()を使うと、エラーにならず値を書き換えることができます。

app.component.spec.ts
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');
  });
});

テスト結果

2つ成功しました。
スクリーンショット 2022-05-28 16.10.22.png

おわりに

今回は、readOnlyの値を強制的に書き換える方法として、Object.defineProperty()を紹介しました。
よい方法なのか、確証がないので使う場合は自己判断でお願いします!

ちなみに、この方法は、拡張元でなくテスト対象のComponentの読み込み専用プロパティにも使用できます。あればsetterを使って実装するのが、本来のコードの実行に近くてふさわしいと思います。

また、テストでない実際の実装にも使えるはずですが、使おうとした場合設計がおかしいと思います。その場合、設計を見直すことをオススメします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?