2016-09-14 134 views
2

使用TestBed,我们能够为依赖注入提供的类创建模拟类。例如,MyButtonClass可以访问ElementRefMyService,因为它们是通过依赖注入实现的,所以我们可以覆盖它们。我遇到的问题是,为了编写Jasmine测试,我必须创建模拟类来覆盖不使用依赖注入进行访问的类的方法。Angular 2 TestBed,无依赖注入的模拟方法

在这种情况下,ScriptLoader.load将在全局空间中加载ThirdPartyCheckout。这意味着,当Jasmine读取订阅运营商内部的内容时它可能不可用。出于这个原因,我想嘲笑前者,然后是后者。或者也许有不同的方式来解决这个问题。

如果有人可以建议一种创建模拟类以覆盖ScriptLoader.load方法和ThirdPartyCheckout.configure方法的方法,那将会很棒。

的指令进行测试:

@Directive({ 
    selector: '[myButton]' 
}) 
export class MyButtonClass implements AfterViewInit { 

    private myKey: string; 

    constructor(private _el: ElementRef, private myService: MyService) {} 

    ngAfterViewInit() { 
     this.myService.getKey() 
      .then((myKey: string) => { 
       this.myKey = myKey; 
       ScriptLoader.load('https://thirdpartyurl.js', this._el.nativeElement) 
        .subscribe(
         data => { 
          this.handeler = ThirdPartyCheckout.configure(<any>{ 
           key: this.myKey 
           // etc 
           // and some methods go here 
          }); 
         }, 
         error => { 
          console.log(error); 
         } 
        ); 
     }); 
    } 
} 

下面是测试代码:

@Component({ 
    selector: 'test-cmp', 
    template: '' 
}) 
class TestComponent {} 

class mockMyService { 
    getKey() { 
     return Promise.resolve('this is a key in real code'); 
    } 
} 

describe('myButton',() => { 
    beforeEach(() => { 
     TestBed.configureTestingModule({ 
      declarations: [TestComponent, MyButtonClass], 
      providers: [ 
       {provide: MyService, useClass: mockMyService} 
      ] 
     }); 
    }); 

    describe('ngAfterViewInit', fakeAsync(() => { 
     const template = '<div><div myButton></div></div>'; 
     TestBed.overrideComponent(TestComponent, {set: {template: template}}); 
     let fixture = TestBed.createComponent(TestComponent); 
     fixture.detectChanges(); 
     tick(); 
    })); 
}); 

回答

2

功能是一等公民,你可以指定一个新的功能,它

let originalFn; 

beforeEach(() => { 
    originalFn = ScriptLoader.load; 
}); 

afterEach(() => { 
    ScriptLoader.load = originalFn; 
}); 

it('...', fakeAsync(() => { 
    ScriptLoader.load = (url, el: Element): Observable<string> => { 
    return Observable.of('HelloSquirrel'); 
    }; 
    ... 
})); 

除此之外,您可能只想考虑使用DI。使用DI的主要原因之一是更好的可测试性。对于ScriptLoader只是使该方法成为一种非静态方法,而对于第三方lib而言,只需为其创建抽象服务层即可。

+0

我简单地在'beforeEach()'里面覆盖'ScriptLoader.load'并工作。谢谢。但是,你用'beforeEach'和'afterEach'所做的每一个'it'每次都会覆盖ScriptLoader.load并将它返回给一个空变量,对吗?那是什么意思?也许,我没有区分覆盖静态方法和为函数分配新函数的区别? – jayscript

+0

只需重置它,以便您可以在每个测试中使用不同的方法。如果你想让所有测试都一样,你可以使用'beforeAll'和'afterAll'。请记住,静态方法与类保持一致,而不是实例。所以在这个测试中改变它会影响使用它的其他测试(文件)。所以我们希望在完成我们的测试之前将其重置为原始方法 –

+0

_“我没有区分覆盖静态方法和为函数分配新函数的区别吗?” - 不确定覆盖是什么意思。我认为你指的是同样的东西 –