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

Jasmine Comparison Showdown: toEqual() vs toBe()

I was doing a code review for a colleague recently and noticed his test used toEquals() to compare two separate arrays. Wait! What? You can't do that? I checked out the code and stepped through it using IntelliJ's debugger. Indeed the code did work. I needed to find out what toEquals() was! Up until then, I'd primarily focused on toBe() for comparisons in unit tests. When I needed to compare an array or array items, I'd often dig deep and compare individual elements, or properties on such elements. I'm a bit embarrassed I never knew there was a better / easier way. Let's look at toEqual() along with a dose of ToBe().

Which is Which?

  • >toBe(): The toBe() comparison will compare two references. If they are equal, you'll get a success and if not, you'll get a failure. It uses `===` under the hood.
  • >toEqual(): The toEqual() compares values in the object. It is a deep equality comparison.

For simple values, both toBe() and toEquals() are essentially identical. toEqual() becomes much more interesting when dealing with objects, or arrays.

Object Comparison

Object Comparisons are pretty easy to set up in a test. First, create the test outline:


it('Should compare Objects', () =>
{
});

Now, create two objects in the test:


const obj1 = {id: 1, foo: 'bar'};
const obj2 = {id: 1, foo: 'bar'};

We have two objects, and therefore two object instances, but the properties and values inside each object are identical.

Run these two assertions:


expect(obj1).not.toBe(obj2); // reference compare; not equal
expect(obj1).toEqual(obj2); // property compare; equal

The toBe() comparison checks for references. Both obj3 and obj4 are pointed at different objects, and as such the compare will fail. I added a not to the compare so it would succeed.

The toEquals() comparison will check the values in each object. Since they match up, and all have equivalent values on each property, it passes.

If there is any difference between the properties of the two objects the toEquals() compare will fail. Let's add a third object:


const obj3 = {id: 1, foo: 'bar', foo2: 'bar2'};

And a new assertion:


expect(obj1).not.toEqual(obj3); // property compare; not equal

Since the third object has a new property, the two objects are not comparatively equal.

Array Comparison

This use case is more interesting to me. You can use toEqual to compare the objects in two separate arrays.

As with the previous sample, let's start with two objects:


const obj1 = {id: 1, foo: 'bar'};
const obj2 = {id: 2, foo: 'bar2'};

And create two arrays from these objects:


const array1 = [obj1, obj2];
const array2 = [obj1, obj2];

Each array is different, but has the exact same objects in the exact same order.

Normally in a situation like this I'd drill down into the array elements, possibly accessing specific properties to determine equality:


expect(array1[1]).toBe(array2[1]); // reference compare, equal

It works, but is not the most elegant way.

Comparing the two arrays with toBe() will cause a failed assertion:


expect(array1).not.toBe(array2); // reference compare; not equal

I make this pass by using the 'not' operator. Using toEqual() on the other hand succeeds:


expect(array1).toEqual(array2); // element compare; same references, equal

That is awesome.

The order of elements in the array must be equal. We can add another array:


const array3 = [obj2, obj1];

It contains the same objects, but in the reverse order. The comparison will fail:


expect(array1).not.toEqual(array3); // element array; different order, not equal

The equals() comparison is drilling down into objects for specific values. In the array it is not comparing references.

Let's create a third object:


const obj3 = {id: 1, foo: 'bar'};

The 3rd object is the same as our first object, in terms of values. Let's also add a fourth array:


const array4 = [obj3, obj2];

The fourth array is different from array1, in that it references a different array instance, and in the fact that the first element is a different object. However, since obj3 and obj1 are similar, we can still compare with toEquals() succesfully:


expect(array1).toEqual(array4);

I can definitely see using this in future tests more than I have in the past.

I threw together some samples using Angular, because that was the TypeScript framework I had easily available to set up some quick experimental tests.

Check out the code sample.

How to Spy on a Private Method with a Jasmine

I was writing some tests for an Angular project. I was testing a public method, but that public method called a private method which returned some an Observable. I wanted to spy on the method, and control the returned Observable, so I could test the internal subscribe() results. Trying to spy on the private method in the usual way caused issues. This post will explain the issues and how I got around it.

The Setup

For the purposes of this sample, you can find the code in this repo. But if you want to follow along and build from scratch, I started by creating a new Angular project with the Angular CLI.

Then I opened up the `app.component.ts` project. Create one private method:


private myPrivateMethod(value) {
return value;
}

And create one public method:


myPublicMethod(value) {
return this.myPrivateMethod(value);
}

This demonstrates a public method calling the private method, which is enough for the purposes of this sample.

Write the Broken Spy

The first step in testing an Angular application is to create the TestBed, which is done by default with the Angular CLI, so we can move onto our test:


it('should spy on method', () =>
{
const fixture = TestBed.createComponent(AppComponent);
const app: AppComponent = fixture.debugElement.componentInstance;
});

Get the fixture, and then an instance of the AppComponent. If you're not using Angular as a framework, the syntax will probably be as simple as:


const myClass = new MyClass();

Now, add a spy. Normally I'd do something like this:


spyOn(app, 'myMethod');

Your IDe will probably show you an error:

The solution is to add an `` distinguisher to remove the error:


const privateSpy = spyOn<any>(app, 'myPrivateMethod');

Now, you can call your public method:


const returnValue = app.myPublicMethod('something');

And then call your assertions. There are a handful of different ways to check the spy. This would be the easiest:


expect(app['myPrivateMethod']).toHaveBeenCalled();

Instead of using object property notation to make sure the method was called, we use array notation with a literal string. This is because private methods don't exist in JavaScript. This notation gets around the TypeScript compile warnings that would occur of we tried to access `app.myPrivateMethod()`.

You can also check the myPrivateSpy variable directly:


expect(privateSpy).toHaveBeenCalled();

This works great, and is identical to the previous line.

You could also drill into the spy object:


expect(privateSpy.calls.any()).toBeTruthy();

Although, my usual mode of operation would be to use the first two methods.

spyOn with will work just like a spy on without it. So you can return values, or callThrough when creating the spy. Or you could use any of the various spy tests, such as `toHaveBeenCalledWith()`, `toHaveBeenCalledTimes()`, or others.

You can 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 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 you Access the Arguments of a Jasmine spy?

I've been writing unit tests for clients. It's a nice change to finally have some clients who care about such things.

I have this Angular method that creates an object, then emits an event.

The method was something like this:


onButtonClick() {

let eventParameters = {};

// do stuff

this.eventEmitter.emit(eventParameters);
}

I want to be compare the properties of the eventParameters argument with what I expect them to be. However, I have no way to access the eventParameters directly because it is local to the method. My first thought was to do something like this:


spyOn(component,'onButtonClick').and.returnValue(myOwnEventParametersObject)

However my intent was to test the code inside the method, whereas this bypasses calling the method altogether. That would not suffice.

So, how'd I do it?

First, I saved an instance to the Spy:


const mySpy = spyOn(component.eventEmitter, 'emit');

I can drill down into this spy to get the results of the most recent call:


const recentCallArgs = mySpy.calls.mostRecent().args[0];

Once I have the argument object, I can introspect it to compare the arguments created by the method with what I expected them to be.


expect(recentCallArgs.value).toBe('value');

TaDa!

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.

How do you tell Jasmine to run a single test?

Last week I wrote about how to tell Jasmine to ignore a unit test. This week I'll tell you how to ignore every unit test except one.

To use last week's example, a normal unit test would be something like this:


it("True is True", () =>
{
expect("True").toBe("True");
});

A normal test suite will include lots of tests spread out through lots of files. You can focus a unit test using fit(). This tells Jasmine to only run the unit test with the 'f':


fit("True is True", () =>
{
expect("True").toBe("True");
});

other unit tests will be ignored. This can be a great use when testing something new.

You can use the same thing to disable a full suite with fdescribe() instead of describe:


fdescribe("Some Test Suite"()=>
{
it("True is True", () =>{
expect("True").toBe("True");
});
});

This type of stuff can be pretty handy if you're running your tests via command lines.

More Entries

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.