2

I'm working on an Angular 2-rc3 application and I have some unittests set up and they're working, yay! I just don't understand why they have to be written the way they do. And even more amazing, all the examples I see have the same approach. Specifically, these questions are on the top of my list:

  1. Why is the TestComponentBuilder configured in every unittest?

    it('shows list of blog items by default', inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
        return tcb
        .overrideProviders(BlogRoll, [provide(BlogService, {
                useValue: mockBlogService
            })])
            .createAsync(BlogRoll)
            .then((fixture) => {
            // actual test code
        });
    });
    

    That's already seven lines of code extra per unittest and the readability of my code suffers a lot from this. I've tried placing this in a beforeEach():

    beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
        console.log('beforeEach');
        return tcb.overrideProviders(BlogRoll, [provide(BlogService, {
                useValue: mockBlogService
            })])
            .createAsync(BlogRoll)
            .then(fixture => {
                // this never gets printed
                console.log('in then:', fixture.componentInstance);
            });
    }));
    

    but Karma doesn't seem to be able to handle the asyncness, everything in the then just doesn't get executed. Is this a bug or is this intended, are we not supposed to do it like this?

  2. Why does the creation of this component need to happen asynchronous? There's a createSync() on the TestComponentBuilder class, can't we use that? Of course I tried it and found out that the function signatures differ: createAsync(rootComponentType: Type) : Promise<ComponentFixture<any>> and createSync(componentFactory: ComponentFactory<C>) : ComponentFixture<C>. Why do we need a component factory here, why don't we need it when creating the component async? // Update: RC4 is out and createSync() now accepts a Type. Great.

My sanity thanks you already!

Community
  • 1
  • 1
J.P.
  • 5,567
  • 3
  • 25
  • 39
  • Update: The changelog for RC4 contains this breaking change: `compiler: TestComponentBuilder.createSync now takes a component type and throws if not all templates are either inlined are compiled before via createAsync.` – J.P. Jul 01 '16 at 17:16

2 Answers2

3

Angular 2 final has shipped and the TestComponentBuilder class has been replaced by the TestBed, which confuses me a whole lot less.

J.P.
  • 5,567
  • 3
  • 25
  • 39
1
  1. The angular2-material tests are nicely organised, and setup TestComponentBuilder in the beforEach, although they do still call createAsync for each test. You can then nest the createAsync call like this:

    describe('Example', () => {
            let fixture;
            let builder: TestComponentBuilder;
    
            beforeEach(injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
                    builder = tcb;
    
                    builder.createAsync(ExampleComponent).then(        
                         fixture = f;
                    });
            });
    
    
            it('Test 1', () => {
                         expect(fixture.componentInstance).not.toEqual(null);
            });
    });
    
Edd
  • 25
  • 4
  • Did you test this code? Your `then` contains a syntax error (`f => {` is missing). One major difference with my code is that you're using `injectAsync()` which I hadn't tried yet, but unfortunateliy, on my machine the results are the same and the `then` does not get executed whatsoever. Does it get executed with you? – J.P. Jun 29 '16 at 08:46
  • Hold the phone! I get an actual error: `No Directive annotation found on MyPipe`. No idea why though. My application is working, but my test can't find my pipe? The test is in the same directory as my pipe, `import { MyPipe } from './my.pipe';` is correct... Weird. – J.P. Jun 29 '16 at 09:05