Page 74 - MSDN Magazine, May 2017
P. 74

Figure 1 CopyrightComponent
go a long way against confusion over time as more and more components are developed, debugged and deployed.
Testing, Testing
Speaking of debugging, one of the things scaffolded out is the .spec.ts file that defines a basic set of unit tests for the component, invoked as part of the test suite that gets executed when running either npm test or ng test. (The test rig is set up to automatically pick up all .spec.ts files under the app subdirectory, so it picks up FooterComponent tests the moment the file is introduced.) Without going into too much detail (yet) about the tests, Figure 2 shows what FooterComponent tests look like.
To those readers who have spent some time writing unit tests in other frameworks, this won’t seem too entirely indecipherable; to wit: • describe establishes a unit test suite, which doubles as a scope/ context in which all the tests defined therein will run. It pro- vides a place, for example, to hang fields used as part of each test. (Never try to use these across tests; always assume—and
ensure—that the fields are reset before and after each test.) • beforeEach, as its name implies, is a block of code that runs
before each test.
Figure 2 FooterComponent Test
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'footer',
templateUrl: './footer.component.html', styleUrls: ['./footer.component.css']
})
export class FooterComponent implements OnInit {
currentYear: string; @Input() owner: string;
constructor() { }
ngOnInit() {
var d = new Date();
this.currentYear = d.getFullYear().toString(); console.log("Current year is ", this.currentYear);
} }
The generated files aren’t anything magical, and you’ve seen them before: The footer directory contains the FooterComponent scaffolded code, and includes a TypeScript source file, a CSS and HTML file for the UI, and a .spec.ts file specifically for testing this component. It has also updated the application module file (and, sure enough, if you look in that file, it’s been updated to include the footer files and import directive). If the server is still running and you have a browser window open to it, then there will be a brief flicker of “Loading ...” and then the application will show up again. (Nothing new is dis- played because the UI hasn’t changed, but the flicker is the application being reloaded because of the modifications to the app.module.ts file.) Because it’s easiest to imagine how you’ll want to use this particular component, let’s start with the HTML template in the footer.com- ponent.html file. And, in fact, let’s start with how you want to use it.
Fundamentally, I’m thinking that the footer will want to dis- play something along the lines of “Copyright (c) 2017, Neward & Associates, LLC,” which suggests two variabilities in the message displayed: the current year and the copyright owner. One will be generated internally within the component (by creating a new Date object and extracting the year) and one will be passed in from the outside, like so (from app.component.html):
<h1> {{title}}
</h1>
<footer owner="Neward & Associates, LLC" />
This suggests that the FooterComponent code needs an input variable (as I discussed in last month’s column) and then also an internal currentYear variable that will be reflected in the HTML (footer.component.html):
<p>Copyright (c) {{currentYear}} {{owner}}</p>
So now, all that remains is to populate the right variables in the FooterComponent itself (footer.component.ts), as shown in Figure 1. Note that the “selector” value in the @Component decorator defaulted to app-footer and I’ve changed it to footer so as to match the HTML usage I want; this is obviously an aesthetic argument in terms of what the tags should look like. (Some may prefer the app- prefix, but I find it a little redundant.) Whatever your prefer- ence, the selector value will define the syntax used in the HTML templates used by other components, so a consistent “style” will
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>; let de: DebugElement;
let el: HTMLElement;
beforeEach(async(() => { TestBed.configureTestingModule({
declarations: [ FooterComponent ] })
.compileComponents(); }));
beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent); component = fixture.componentInstance;
de = fixture.debugElement.query(By.css('p'));
component.owner = "ACME, Inc"; fixture.detectChanges();
el = de.nativeElement; });
it('should create', () => { expect(component).toBeTruthy();
});
it('should know the current year', () => {
const currentYear = "" + new Date().getFullYear(); expect(component.currentYear).toEqual(currentYear);
});
it('should know the owner', () => { expect(component.owner).toEqual("ACME, Inc");
});
it('should render message in a p tag', async(() => { expect(el.textContent).toContain("Copyright (c)"); expect(el.textContent).toContain(new Date().getFullYear().toString()); expect(el.textContent).toContain("ACME, Inc");
})); });
68 msdn magazine
The Working Programmer


































































































   72   73   74   75   76