【问题标题】:Angular 7 testing public methods dependent on private onesAngular 7 测试依赖于私有方法的公共方法
【发布时间】:2020-01-03 12:56:26
【问题描述】:

我正在使用 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 受到保护,并通过以下方式对其进行测试:

    import { HttpClientTestingModule } from '@angular/common/http/testing';
    import { async, ComponentFixture, TestBed } from '@angular/core/testing';
    
    class AMock extends A {
      ngOnInit() {
        //privateState is now 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 命令后,由于无法读取未定义的属性“testedFunction”,最后一点不起作用。

我不知道哪种方法是正确的测试方法。我知道我可以将hiddenComplexFunction 公开,我所有的问题都消失了,而且我不喜欢这样一个事实,即由于测试我必须更改访问修饰符(从私有到受保护的模拟示例中似乎全部我错了)。

【问题讨论】:

  • 这是一个纯类,还是一个组件?您没有 @Component 装饰器,但正在尝试使用 onInit 并尝试在测试中将其实例化为 componentFixture。如果它是一个类,那将是一个不同的答案,如果它是一个组件。
  • 它是一个组件,我会添加装饰器

标签: javascript angular jasmine


【解决方案1】:

您的问题涉及很多内容,所以我将从我容易看到的内容开始。

我们可以从您的核心组件代码开始。在您的testedFunction() 中,调用this.hiddenComplexFunction()。你想要函数的实例版本,所以需要添加 ""this."" 到它。这是非常合适的,无论如何您都不想直接测试私有函数/私有属性。

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

this.state = x['whatever'];

设置您的 privateState,而不是状态

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';  // 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('A', () => {
  let component: A;  // changed from AMock to A
  let fixture: ComponentFixture<A>; // changed from AMock to A

  let mockedState = {something: 'yelp'}; // build your expected state here

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

  beforeEach(() => {
    fixture = TestBed.createComponent(A); // changed from AMock to 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;

在每个之前 -

backend = TestBed.get(HttpTestingController);

在您的 IT 声明中 -

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

backend.verify();

让我们知道这对您有多大帮助,是否有帮助或您是否遇到其他错误。

【讨论】:

  • 这似乎解决了我的大部分问题,有什么办法可以存根任何地址而不是在 expectedURL 中指定一个地址?
  • 我猜使用backend.expectOne(() =&gt; true).flush(mockedState); 不是正确的方法
  • 为了测试你的请求,你可以试试这个 - const backendRequest = backend.expectOne((request: HttpRequest) => true);然后在 backendRequest 上调用 expect 以确保您的请求看起来不错。 expectOne 的箭头函数可以做很多事情。查看使用 HttpClientTestingModule 标头(我的网站)设置我们的第二个规范下的 angular.io/api/common/http/testing/HttpTestingControllercoder.haus/2019/10/03/easy-angular-testing-services 以获得想法。
  • 另外,对您的代码的优化 - 从 ngOnInit 中删除功能。生命周期方法很难测试。将该代码添加到 getData() 或其他命名方法中,然后从您的 ngOnInit 调用该方法。然后,您可以轻松地测试该方法中的数据检索,以及能够模拟该方法以测试依赖它的其他代码。
猜你喜欢
  • 2019-12-11
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-17
相关资源
最近更新 更多