Angular 7 测试依赖于私有方法的公共方法

huangapple go评论244阅读模式
英文:

Angular 7 testing public methods dependent on private ones

问题

Here's the translation of the code and the problem you're facing:

我正在使用 Angular 7.1 和 Jasmine 来测试我的组件。我尝试编写一些测试,但这个似乎对我来说有点困难。

我要测试的简化类如下:

import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'a',
  templateUrl: './a.html',
  styleUrls: ['./a.scss']
}
export class A implements OnInit {
  constructor(private http: HttpClient) {}
  private privateState: State;
  public publicState: State2;      

  ngOnInit() {
      this.http.get(this.GetUrl()).subscribe((x) => {
      this.privateState = x['whatever'];
    });
  }

  private hiddenComplexFunction() {
    this.publicState = this.privateState.something;
  }

  public testedFunction() {
    //someComplex Code
    this.hiddenComplexFunction();
  }
}

我尝试过以下方法:

  • 我尝试像这样设置私有变量 A['privateState'],但没有起作用。
  • 我尝试使用 SpyOn http get,但遇到了与设置私有属性相同的问题。
  • 我尝试使用 HttpClientTestingModule,但与前面的问题相同。
  • 最后,我尝试将 privateState 改为 protected 并以以下方式进行测试:
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

class AMock extends A {
  ngOnInit() {
    // privateState 现在是 protected
    this.privateState = mockedState;
  }
}
describe('A', () => {
  let component: AMock;
  let fixture: ComponentFixture<AMock>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      declarations: [AMock]
    })
      .compileComponents().catch();
  }));

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

  it('testedFunction should set up public state correctly', () => {
    component.testedFunction();

    expect(component.publicState).toEqual(mockedState.something);
  });
});

但最后一个方法在运行 ng test 命令后返回错误,错误消息是 Cannot read property 'testedFunction' of undefined。

我不知道哪种方法是正确的测试方式。我知道我可以将 hiddenComplexFunction 设为公共的,这样我的问题都会解决,但我不喜欢因为测试而必须更改访问修饰符(从私有改为受保护,就像模拟示例中一样,这对我来说似乎都是不正确的)。

英文:

I am using angular 7.1 and Jasmine to test my components. I am trying to write some tests but this one seems to be too much for me.

Simplified class I am trying to test:

import { HttpClient } from &#39;@angular/common/http&#39;;

@Component({
  selector: &#39;a&#39;,
  templateUrl: &#39;./a.html&#39;,
  styleUrls: [&#39;./a.scss&#39;]
}
export class A implements OnInit {
  constructor(private http: HttpClient) {}
  private privateState: State;
  public publicState: State2;      

  ngOnInit() {
      this.http.get(this.GetUrl()).subscribe((x) =&gt; {
      this.privateState = x[&#39;whatever&#39;];
    });
  }

  private hiddenComplexFunction() {
    this.publicState = this.privateState.something;
  }

  public testedFunction() {
    //someComplex Code
    this.hiddenComplexFunction();
  }
}

What I've tried:

  • I tried to set up the private variable like A.[&#39;privateState&#39;] but did not work

  • I tried to use SpyOn http get but had the same issue with having to set up a private property

  • I tried to user HttpClientTestingModule but I had the same issue as with the previous point

  • lastly I tried to make privateState protected and test it in the following way:

     import { HttpClientTestingModule } from &#39;@angular/common/http/testing&#39;;
     import { async, ComponentFixture, TestBed } from &#39;@angular/core/testing&#39;;
    
     class AMock extends A {
       ngOnInit() {
         //privateState is now protected
         this.privateState = mockedState;
       }
     }
     describe(&#39;A&#39;, () =&gt; {
       let component: AMock;
       let fixture: ComponentFixture&lt;AMock&gt;;
    
       beforeEach(async(() =&gt; {
         TestBed.configureTestingModule({
           imports: [HttpClientTestingModule],
           declarations: [AMock]
         })
           .compileComponents().catch();
       }));
    
       beforeEach(() =&gt; {
         fixture = TestBed.createComponent(AMock);
         component = fixture.componentInstance;
       });
    
       it(&#39;testedFunction should set up public state correctly&#39;, () =&gt; {
         component.testedFunction();
    
         expect(component.publicState).toEqual(mockedState.something);
       });
    

the last point did not work with an error coming back as Cannot read property 'testedFunction' of undefined after running ng test command.

I don't know which way would be the right way of testing this. I know I can make hiddenComplexFunction public and all my problems go away and also I don't like the fact that because of tests I would have to change the access modifiers (from private to protected like in the mocked example which just seems all wrong to me).

答案1

得分: 0

以下是您要翻译的内容:

"在您的问题中涉及很多内容,所以我会从我能看到的部分开始。

我们可以从您的核心组件代码开始。在您的testedFunction()中,调用this.hiddenComplexFunction()。您需要调用该函数的实例版本,所以需要在它前面添加"this."。这是完全合适的,而且您也不希望直接测试私有函数/私有属性。

接下来,我猜您想要这段代码:

this.state = x['whatever'];

来设置您的privateState,而不是state

this.privateState = x['whatever'];

接下来,您不需要使用AMock类来模拟A,测试框架会为您完成这个任务。可以尝试以下代码作为开始。我没有包含在这里的部分是与状态有关的代码。我实际上只是去掉了您的AMock,并将其替换为实际的组件,并构建了一个测试状态(而且我可能还构建得不太好,哈哈!)。下面我会处理HTTP数据,根据您的代码推测,这与您的状态有关。

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { A } from './zing.component';  // 这应该指向您的组件

describe('A', () => {
  let component: A;  // 从AMock改为A
  let fixture: ComponentFixture<A>; // 从AMock改为A

  let mockedState = {something: 'yelp'}; // 在这里构建您期望的状态

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      declarations: [A] // 从AMock改为A
    })
      .compileComponents().catch();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(A); // 从AMock改为A
    component = fixture.componentInstance;
  });

  it('testedFunction should set up public state correctly', () => {
    component.testedFunction();

    expect(component.publicState).toEqual(mockedState.something);
  });
});

最后,关于您的HTTP模拟,您可以使用HttpTestingController将一些数据传递给您的组件。

在描述块中:

let backend: HttpTestingController;

在每个before each中:

backend = TestBed.get(HttpTestingController);

在您的it语句中:

backend.expectOne(expectedURL).flush(mockedState);

backend.verify();

请告诉我们这些内容对您有多大帮助,如果有用或者是否出现其他错误。"

英文:

There's a lot going on in your question, so I'll start with things I can readily see.

We can start with your core Component code. In your testedFunction(), call this.hiddenComplexFunction(). You want the instance version of the function, so need to add ""this."" to it. It's perfectly appropriate, and you wouldn't want to directly test the private functions/private properties anyway.

Next, I'm guessing you wanted this code

this.state = x[&#39;whatever&#39;];

to set your privateState, not state

this.privateState = x[&#39;whatever&#39;];

Next, you don't need to mock A with your AMock class, the test rigging will do that for you. Try something like below for a start. The bit I don't have in here is the State code. All I really did was get rid of your AMock and replaced it with the actual component, and build a test state (and poorly built the state, I might add. Ha!). I tackle the HTTP data below, which would be related to your state I'm guessing by looking at your code.

import { HttpClientTestingModule } from &#39;@angular/common/http/testing&#39;;
import { async, ComponentFixture, TestBed } from &#39;@angular/core/testing&#39;;
import { A } from &#39;./zing.component&#39;;  // this should point to your component

// get rid of this, not needed
// class AMock extends A {
//   ngOnInit() {
//     //privateState is now protected
//     let privateState = mockedState;
//   }
// }

describe(&#39;A&#39;, () =&gt; {
  let component: A;  // changed from AMock to A
  let fixture: ComponentFixture&lt;A&gt;; // changed from AMock to A

  let mockedState = {something: &#39;yelp&#39;}; // build your expected state here

  beforeEach(async(() =&gt; {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      declarations: [A] // changed from AMock to A
    })
      .compileComponents().catch();
  }));

  beforeEach(() =&gt; {
    fixture = TestBed.createComponent(A); // changed from AMock to A
    component = fixture.componentInstance;
  });

  it(&#39;testedFunction should set up public state correctly&#39;, () =&gt; {
    component.testedFunction();

    expect(component.publicState).toEqual(mockedState.something);
  });
});

And finally for your HTTP mock, you can use HttpTestingController to flush some data to your component.

in Describe block -

let backend: HttpTestingController;

before each -

backend = TestBed.get(HttpTestingController);

in your IT statement -

backend.expectOne(expectedURL).flush(mockedState);

backend.verify();

Let us know how far this get's you, if it's helpful or if you get other errors.

huangapple
  • 本文由 发表于 2020年1月3日 20:56:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/59579039.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定