Check out our Angular Book Series.

How do I test nativeElement.focus in Angular?

In last week's post I wrote about setting focus on an element with Angular.

This week I wanted to write tests behind that code.

A Review

First, let's review what we want to test. We have a loop of inputs in the HTML Template and access them in the component class using the ViewChildren() metadata:


@ViewChildren('input') inputs: QueryList<ElementRef>;

Then we have an onClick() method which accepts an input and determines which element to set focus too:


onClick(index) {
this.inputs.toArray()[index].nativeElement.focus();
}

As a good developer, I of course, want to write tests against that onClick() method.

Write the Tests

If you're building off last week's project, you can open up the app.component.spec.ts file. You probably already have a beforeEach() which creates the basic application. Let's add a new describe block to the file:


describe('onClick()', () =>
{
});

Good start, let's add some variables:


let app;
const e1: ElementRef = new ElementRef({focus() {} } );
const e2: ElementRef = new ElementRef({focus() {} } );

I created one app variable to reference the component, and two ElementRef constants. When creating an ElementRef we pass in a nativeElement object. For the purposes of this test, I created a mock object with a focus() method. In production code I would have formalized a mock class to represent a nativeElement.

Now, let's add a beforeEach:


beforeEach(() =>
{
});
</code>

Nothing interesting here. Inside the beforeEach() let's get access to the app:

<code
const fixture = TestBed.createComponent(AppComponent);
app = fixture.debugElement.componentInstance;

This gets access to the fixture and saves the app for future use. Now detectChanges():


fixture.detectChanges();

This forces the component to render once, which will create all the viewChildren and populate the inputs variable.

Now, add spys for the focus method on our custom elementRef objects:


spyOn(e1.nativeElement, 'focus');
spyOn(e2.nativeElement, 'focus');

Our code will check on these spys to make sure the appropriate element was focused on based on our inputs in the onClick() method.

I want to do one more spy:


spyOn(app.inputs, 'toArray').and.returnValue([e1, e2]);

When our onClick() method tries to convert the inputs into an array, this gives us complete control over what the onClick() method is accessing. Even though the elements in the view are properly rendered, using this spy with controlled return values gives us granular control over the test.

Now let's write a test:


it('should call focus on element 0', () =>
{
app.onClick(0);
expect(e1.nativeElement.focus).toHaveBeenCalled();
expect(e2.nativeElement.focus).not.toHaveBeenCalled();
});

This call s the onClick() method with a 0 index. It verifies that focus was called on e1, but not on e2.

We can write a second test using a different index value:


it('should call focus on element 1', () =>
{
app.onClick(1);
expect(e1.nativeElement.focus).not.toHaveBeenCalled();
expect(e2.nativeElement.focus).toHaveBeenCalled();
});

This calls the same onClick() method, with a different index, and swaps the checks making sure that focus() was called on the e2 element and not the e1 element.

Run the code:

Success!

That is one way to test how to set focus on an element using Angular.

Play with the code here

My Unit Tests are Running out of Order with Angular 8!

This is a weird one. I'm updating Unit Test from my LearnWith series for Angular 8. And I ran into a few tests that would inconsistently pass.

The app behind the LearnWith series is a task manager, and one of the tests that was failing was to mark a task completed.

Let's look at the test:


describe('completeTask() ', function () {
let task : TaskVO = {taskCategoryID:1,taskID:10,completed:true} as TaskVO;
};

This is a describe() block, for the completeTask() method and it also creates a default task.

I had two tests in this block. The first makes sure the task was completed:


it('Make task Completed', () =>
{
taskService.completeTask(task).subscribe(
value => {
expect(value.resultObject[0].completed).toBeTruthy();
});
const req = httpMock.expectOne(url) ;
req.flush({"resultObject":[task],"error":0});
}

I truncted it for simplicity. But, it takes the full task--defined as a constant in the describe block--passes that task into a service, creates a request, resolves it, and checks the value returned.

The same test suite had a test to mark a task not completE:


it('Make task Not Completed', () =>
{
task.completed = false;
taskService.completeTask(task).subscribe(
value => {
expect(value.resultObject[0].completed).toBeFalsy();
});
const req = httpMock.expectOne(url) ;
req.flush({"resultObject":[task],"error":0});
}

The second test sets the completed value on the task object to false; then runs very much the same code, and after the service is resolved; makes sure that the completed value is false.

The second test always ran without problems, but the first test would sporadically fail. My only explanation for this is that the tests must not be run in the same order on every execution of the test suite.

The task object is not reinitialized after every it() test, therefore the values can stay constant across multiple tests.

There are two solutions to this. The first is to make sure the first test sets the default value:


it('Make task Completed', () =>
{
task.completed = true;
// rest of test

That magically worked it.

The second solution is to move the task initialization into a beforeEach() block so it would be re-initialized before each test. That would look like this:


let task : TaskVO;

beforeEach(() =>
{
task = {taskCategoryID:1,taskID:10,completed:true} as TaskVO;
});

Either solution would solve the problem; and throughout my various tests I used one or the others depending upon what was failing.

Honestly, I'm surprised that this was not happening in versions of Angular prior to 8, so it took me a bit off guard.

How to spy on a Property with Karma?

When writing unit tests, SpyOn, is used to watch and manipulate a method call, potentially returning your own data. This is used to easily mock external component calls so that your unit test can focus on the class in question without worrying about complications or side affects from external dependencies. I found myself in a situation where I had a class and that class had a read only get() property. I was creating my own Angular service wrapper for the Dragula drag and drop library. I was exposing some drop and remove listeners get methods.

I wanted to control the results returned from that get method. This was not a situation where I could set the underlying value because there was no set method. But, spyOn would not work because a get() method is viewed as a property, not a method. The class code was conceptually like this:


export class MyClass {

constructor(private dragula: DragulaService){
}

get dropListener(){
return this.dragula.drop('myDragulaGroup');
}
}

My code was using Angular, so the DragulaService was injected into this component, and the dropListener was exposed via a get.

For the purposes of unit testing, I want complete control over that drop listener Observable. Thankfully, spyOnProperties would give me that control.

The unit test would be a bit like this:


it("Should spy on Observable", () =>
{
const myClassInstance = new MyClass();

let dropObserver: Observable<any>;
const dropObservable: Observable<any> = Observable.create( ( ObserverArg: Observer<any>) => {
dropObserver= ObserverArg;
}

mySpy = spyOnProperty(myClassInstance, 'dropListener', 'get').and.returnValue(dropObservable);

I'm gonna take a quick break to explain spyOnProperty. It takes three parameters:

  • Class: The class instance that contains the property you want to watch.
  • Property: The name of the property being watched.
  • Accessor: The Access Type of the method being watched, in this case get.

Note that we are storing the results of the spyOnProperty in the mySpy variable. This is optional when using spyOn, but for spyOnProperty it is required. The unit test could would continue:


const val = myClassInstance.dropListener;

All that line does is access the dropListener property inside the class, which should trigger the spy. Now we can test against the access:


expect(mySpy ).toHaveBeenCalled();
});

In a more advanced unit test, we could subscribe to the dropListener inside the test, and use dropObserver.next() to trigger the result handler in an async block.

How do I prevent a Browser from opening up when I run Angular unit tests?

When you run unit tests using the Angular CLI's ng test command you'll often see a browser open up. Sometimes this is useful if you want to debug the unit tests in the browser, however since I have moved the bulk of my unit test debugging to IntelliJ, this is just a minor annoyance.

You can shut it off. Open up the Karma.conf.js file in the src folder of your Angular CLI project. Find this line:


browsers: ['Chrome'],

This tells Karma to open Chrome to run the tests. But, there is another option:


browsers: ['ChromeHeadless'],

Rerun your tests and you'll find that no browser opens up.

I love it when things are easy.

Why won't my Observable Trigger in a Unit Test?

I've been writing some tests as part of a project I'm working on, and for some reason I could not get a mocked Observable to resolve. This will explain the mistake I made and how to fix it.

The Setup

This component used a resolver to load data before the component loaded. To access a resolver's data, you inject the activated route into the constructor, like this:


constructor(private route:ActivatedRoute){}

In the ngOnInit(), you'd subscribe to the data property:


ngOnInit(){
this.route.data.subscribe((data: {myValue}){
this.myValue = data.myValue
}
);
}

This saves your MyValue data from the resolver into the local component instances myValue.

Writing The Tests

First, I mocked the ActivatedRoute:


class ActivatedRouteMock {
data: Observable<Data>;
}

Then, I created an instance of it:


const activatedRoute = new ActivatedRoutMock();

As part of the TestBed's providers:


{ provide: ActivatedRoute, useValue: activatedRoute}

The first test I wrote was to make sure the subscribe was actively working.

First, I created the observer and component:


const observer;
beforeEach(() =>
{
activatedRoute.data = Observable.create (observer) => {
observer = observer;
}
spyOn(activatedRoute.data, 'subscribe');
component = TestBed.createComponent(myComp).componentInstance;
});

Then, I ran the test:


it('should subscribe to activated route', () =>
{
expect(activatedRoute.data.subscribe).toHaveBeenCalled();
});

This worked great. Now, I wanted to test to make sure that other methods inside the component's result function worked. To do that I'll have manually trigger the observer:


it('should resolve route data and save myValue'. ()=>
}
const myValue = "something";
const results = {};
results['myValue'] = myValue;
observer.next(results);
observer.component();
expect(component.myValue).toBe(myValue);
});

This should work, right? Nope, it failed ever time. The observer was always null. I was scratching my head and adding a lot of breakpoints and outputs trying to figure out what I had done wrong so far.

What is wrong

The function to create the observable is never run until the observable is subscribed to. Clearly we are subscribing to it in the component code:


this.route.data.subscribe((data: {myValue}){
this.myValue = data.myValue
}

So, why doesn't it run? Think about it first, because spoilers are below.

The spy is getting in the way:


spyOn(activatedRoute.data, 'subscribe');

Because of the spy in place, the subscribe() function is intercepted, and as such never run, which never runs the function in the Observable's create function, which never saves the Observer. The solution was simple once I figured out what was going on:


spyOn(activatedRoute.data, 'subscribe').and.callThrough();

Tell the spy to intercept and watch, but still allow the call to go through.

Everything started working wonderfully. :-)

How do I test Observable.timer?

This was trickier than I thought, which is why I'm writing a blog post about it. I'm writing some code which uses an FakeAsync Zone nor RxJS TestScheduler.

The timer() call returns an Observable, so I decided to create one myself which gave me complete control.


let timerObserver :Observer<any>;
beforeEach(() =>
{
spyOn(Observable, 'timer').and.returnValue(Observable.create(
(observer =>{
timerObserver = observer;
})
));
});

I created an Observer object. This is the internal logic that makes the Observable resolve itself. I used spyOn() inside a beforeEach() to have the unit testing framework return my own Observable and ignore the library code. I save the observer for later usage.

Now, when I'm testing I can resolve the timer immediately:


it('Some Test',()=>
{
// do stuff if needed

// trigger the fake timer using the Observer reference
timerObserver.next('');
timerObserver.complete();

//
expect(somethingToHappenAfterTimerCompletes).toHaveBeenCalled();
});

I spent more time banging my head on this than I thought I would, and I hope this helps you.

Running Unit Tests from IntelliJ

I put together this screencast about running Unit Tests using IntelliJ.

Unhandled rejection Error: Not running at Server.close (net.js:1236:11) with Karma and Jasmine

I'm writing some addendum to my LearnWith AngularJS training series. One of those will detail how to unit test an AngularJS web application. I'm setting something up with Gulp, Karma, and Jasmine.

Trying to get this running gave me a full day of this error:


Unhandled rejection Error: Not running
at Server.close (net.js:1236:11)
at C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\lib\server.js:388:17
at tryCatcher (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\util.js:16:23)
at Promise._settlePromiseFromHandler (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\promise.js:510:31)
at Promise._settlePromise (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\promise.js:567:18)
at Promise._settlePromise0 (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\promise.js:612:10)
at Promise._settlePromises (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\promise.js:691:18)
at Async._drainQueue (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\async.js:138:16)
at Async._drainQueues (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\async.js:148:10)
at Async.drainQueues (C:\Users\jhouser\Documents\career\clients\ActiveClients\LearnWith\Development\www_codearchive\Scripts_Test\node_modules\karma\node_modules\bluebird\js\release\async.js:17:14)
at process._tickCallback (node.js:419:13)

There is a lot of Google hits for this error; but none actually relate to my issue, so I hope this helps someone.

After much head pounding and experimentation; I finally deciphered the cause. When creating the Karma config file, make sure that your files are put in proper dependency order. Simply put; I was listing the AngularJS library at the end of the file list instead of the beginning, so the code didn't know how to create angular modules, services, or other elements, because that Angular library wasn't defined yet.

Changing the order of dependencies solved the issue and I can run unit tests as expected.

I hope that helps someone.

Sign up for DotComIt's Monthly Technical Newsletter

All Content Copyright 2005, 2006, 2007, 2008, 2009 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.002.