Angular 2 rc5 , unit testing issue with pipes that

2019-02-20 00:10发布

问题:

I am using angular2 rc 5, I have written a custom pipe that fetches value from a json. The custom pipe : literal.pipe.ts looks like :

import {Pipe, PipeTransform, Inject} from '@angular/core';
import {MessageService} from '../service/message.service';

@Pipe({
  name: 'literalString',
  pure: false
})
export class LiteralPipe  implements PipeTransform{

private messageBundle:any;
private request:any;

    constructor(private _messageService: MessageService){
        this._messageService = _messageService;
        this.messageBundle = {};
    }

    transform(value:string, args:string[]):any {
        if(!this.request){
            this.request = this._messageService.getBundle();
            this.request.subscribe(
                (data:any) => this.messageBundle = data
             );
        }

         if(this.messageBundle[value])
             return this.messageBundle[value];
        else if(args[0])
            return args; 
         else
            return "String not available. Pls include in bundle.json";
    }
}

MessageService.ts :

import {Injectable} from "@angular/core"
import {Http} from '@angular/http';
import 'rxjs/Rx'

@Injectable()
export class MessageService{
    http:any;
    constructor(http: Http){
        this.http = http;
    }
    getBundle (){
        return this.http.get('../src/bundle.json').map((res:any) => res.json());
    }
}

Bundle.json has key value pairs as

{
            "Home.title": "Login",
            "DashBoard.Title": "Dashboard",
            "CheckOutGiftCard.Title": "GIFT CARD CHECKOUT"
}

I am running my unit tests using liteServer. My unitTests.html:

<meta http-equiv="content-type" content="text/html;charset=utf-8">
  <title>Ng App Unit Tests</title>
  <link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
  <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
  <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
  <script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
  <script src="node_modules/zone.js/dist/zone.js"></script>

  <script src="node_modules/reflect-metadata/Reflect.js"></script>

  <script src="node_modules/systemjs/dist/system.src.js"></script>

  <script src="systemjs.config.js"></script>
</head>
<body>
  <!-- Unit Testing Chapter #1: Proof of life.  -->
  <script>
    it('true is true', function(){ expect(true).toEqual(true); });
  </script>
  <script>
    // #2. Configure systemjs to use the .js extension
    //     for imports from the app folder
    System.config({defaultJSExtensions:true});
    // #3. Load and configure the TestComponentBuilder.
    // #2. Import the spec files explicitly
    Promise.all([
      System.import('app/literal.pipe.spec')
    ])
    // #3. wait for all imports to load ...
    //     then re-execute `window.onload` which
    //     triggers the Jasmine test-runner start
    //     or explain what went wrong.
    //System.import('app/literal.pipe.spec')
    .then(window.onload)
    .catch(console.error.bind(console));
  </script>
</body>
</html>

literal.pipe.spec.ts is as follows:

import { By }             from '@angular/platform-browser';
import { provide }        from '@angular/core';
import { ViewMetadata }   from '@angular/core';
import {LiteralPipe} from '../pipe/literal.pipe';
import {MessageService} from '../service/message.service';
import {Http} from '@angular/http';
import { TestBed } from '@angular/core/testing';
//import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';

// TestBed.initTestEnvironment(
//     BrowserDynamicTestingModule,
//     platformBrowserDynamicTesting()
//     );


  let pipe: LiteralPipe;
  let msgService: MessageService;
////////  SPECS  /////////////
describe('LiteralPipe', () => {
    beforeEach(()=>{
    [addProviders([MessageService]) ]
  });

 TestBed.configureTestingModule({
   declarations:[MessageService],
    providers: [{provide:MessageService, useClass:MessageService }  ],
    imports: [MessageService, Http]
  });
  it('can get an instance', inject([MessageService], (msgService: MessageService) => {
    //create instance of pipe using msgService as the instance of service. 
    pipe = new LiteralPipe(msgService);
  }));
  it('transforms "Home.title" to "Login"', () => {
    expect(pipe.transform('Home.title', null)).toEqual('Login');
  });
});

It looks like I am going wrong somewhere in the spec file as when I run npm test and view the unitTests that opens in the browser I get the following exception:

LiteralPipe can get an instance
TypeError: Cannot read property 'injector' of null
TypeError: Cannot read property 'injector' of null
    at TestBed._createCompilerAndModule (http://localhost:3000/node_modules/@angular/core/testing/test_bed.js:246:44)
    at TestBed._initIfNeeded (http://localhost:3000/node_modules/@angular/core/testing/test_bed.js:213:39)
    at TestBed.execute (http://localhost:3000/node_modules/@angular/core/testing/test_bed.js:274:14)
    at Object.eval (http://localhost:3000/node_modules/@angular/core/testing/test_bed.js:404:45)
    at attemptSync (http://localhost:3000/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1886:24)
    at QueueRunner.run (http://localhost:3000/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1874:9)
    at QueueRunner.execute (http://localhost:3000/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1859:10)
    at Spec.queueRunnerFactory (http://localhost:3000/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:697:35)
    at Spec.execute (http://localhost:3000/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:359:10)
    at Object.fn (http://localhost:3000/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:2479:37)
LiteralPipe transforms "Home.title" to "Login"
TypeError: Cannot read property 'transform' of undefined
TypeError: Cannot read property 'transform' of undefined
    at Object.eval (http://localhost:3000/app/literal.pipe.spec.js:30:20)
    at attemptSync (http://localhost:3000/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1886:24)

What should I change to make this work.

回答1:

The problem here is that you are using TeseBed incorrectly. The example below is modified version of your literal.pipe.spec.ts file.

Main bit is that you have to reset test environment before initialising it.


    TestBed.resetTestEnvironment();

Once environment is reset the the configureTestingModule has to be configured:


    TestBed
    .initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting())
    .configureTestingModule({providers: [MessageService],imports: [HttpModule]});

import {By}             from '@angular/platform-browser';
import {provide}        from '@angular/core';
import {ViewMetadata}   from '@angular/core';
import {LiteralPipe} from './pipe/literal.pipe';
import {MessageService} from '../services/message.service';
import {Http} from '@angular/http';
import {inject, TestBed} from '@angular/core/testing';
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from "@angular/platform-browser-dynamic/testing";
import {HttpModule} from '@angular/http';

let pipe: LiteralPipe;
let msgService: MessageService;
////////  SPECS  /////////////
describe('LiteralPipe', () => {
  beforeEach(() => {
    // Must reset the test environment before initializing it.
    TestBed.resetTestEnvironment();

    TestBed
      .initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting())
      .configureTestingModule({
        declarations: [],
        providers: [
          MessageService
        ],
        imports: [
          HttpModule
        ]
      });
  });
  
  it('transforms "Home.title" to "Login"', inject([MessageService], (msgService: MessageService) => {
    let pipe = new LiteralPipe(msgService);
    expect(pipe.transform('Home.title', null)).toEqual('Login');
  })
  );
});


Also there is a problem with your pipe implementation itself. You should check if args variable is not null before trying to get value from it.

literal.pipe.ts



    if(this.messageBundle[value])
          return this.messageBundle[value];
        else if(args != null && args[0])
          return args;
        else
          return "String not available. Pls include in bundle.json";