karma jasmine_使用Jasmine和Karma测试Angular(第1部分)

karma jasmine

我们的目标 ( Our goal )

In this tutorial we will be building and testing an employee directory for a fictional company. This directory will have a view to show all of our users along with another view to serve as a profile page for individual users. Within this part of the tutorial we'll focus on building the service and its tests that will be used for these users.

在本教程中,我们将为虚构的公司构建和测试员工目录。 该目录将具有一个视图,以显示我们的所有用户,以及另一个视图,以用作个人用户的个人资料页面。 在本教程的这一部分中,我们将专注于构建将用于这些用户的服务及其测试。

In following tutorials, we'll populate the user profile page with an image of the user's favorite Pokemon using the Pokeapi and learn how to test services that make HTTP requests.

在以下教程中,我们将使用Pokeapi使用用户喜欢的Pokemon的图像填充用户个人资料页面,并了解如何测试发出HTTP请求的服务。

你应该知道什么 ( What you should know )

The primary focus for this tutorial is testing so my assumption is that you're comfortable working with TypeScript and Angular applications. As a result of this I won't be taking the time to explain what a service is and how it's used. Instead, I'll provide you with code as we work through our tests.

本教程的主要重点是测试,因此我的假设是您可以轻松使用TypeScript和Angular应用程序。 因此,我不会花时间解释什么是服务以及如何使用它。 相反,我们将在测试过程中为您提供代码。

为什么要测试? ( Why Test? )

From personal experience, tests are the best way to prevent software defects. I've been on many teams in the past where a small piece of code is updated and the developer manually opens their browser or Postman to verify that it still works. This approach (manual QA) is begging for a disaster.

从个人经验来看,测试是预防软件缺陷的最佳方法。 过去,我曾在许多团队中工作过,其中一小段代码被更新,开发人员手动打开他们的浏览器或Postman以验证其是否仍然有效。 这种方法(手动质量检查)正在引发灾难。

Tests are the best way to prevent software defects.

测试是预防软件缺陷的最佳方法。

As features and codebases grow, manual QA becomes more expensive, time consuming, and error prone. If a feature or function is removed does every developer remember all of its potential side-effects? Are all developers manually testing in the same way? Probably not.

随着功能和代码库的增长,手动质量检查变得更加昂贵,耗时且容易出错。 如果某个功能部件被删除,每个开发人员是否会记住其所有潜在的副作用? 是否所有开发人员都以相同方式手动测试? 可能不是。

The reason we test our code is to verify that it behaves as we expect it to. As a result of this process you'll find you have better feature documentation for yourself and other developers as well as a design aid for your APIs.

我们测试代码的原因是为了验证其行为是否符合我们的预期。 作为此过程的结果,您将发现自己和其他开发人员拥有更好的功能文档以及API的设计帮助。

为什么要因果报应? ( Why Karma? )

Karma is a direct product of the AngularJS team from struggling to test their own framework features with existing tools. As a result of this, they made Karma and have transitioned it to Angular as the default test runner for applications created with the Angular CLI.

Karma是AngularJS团队的直接产品,它努力使用现有工具测试其自身的框架功能。 结果,他们制作了Karma,并将其转换为Angular,以作为使用Angular CLI创建的应用程序的默认测试运行程序。

In addition to playing nicely with Angular, it also provides flexibility for you to tailor Karma to your workflow. This includes the option to test your code on various browsers and devices such as phones, tablets, and even a PS3 like the YouTube team.

除了可以很好地使用Angular,它还为您提供了灵活性,使您可以根据工作流程定制Karma。 这包括在各种浏览器和设备(例如手机,平板电脑,甚至YouTube团队等PS3)上测试代码的选项。

Karma also provides you options to replace Jasmine with other testing frameworks such as Mocha and QUnit or integrate with various continuous integration services like Jenkins, TravisCI, or CircleCI.

Karma还为您提供了用其他测试框架(例如MochaQUnit)替换Jasmine或与各种持续集成服务(例如JenkinsTravisCICircleCI)集成的选项

Unless you add some additional configuration your typical interaction with Karma will be to run ng test in a terminal window.

除非您添加一些其他配置,否则与Karma的典型交互将是在终端窗口中运行ng test

为什么选择茉莉花? ( Why Jasmine? )

Jasmine is a behavior-driven development framework for testing JavaScript code that plays very well with Karma. Similar to Karma, it’s also the recommended testing framework within the Angular documentation as it's setup for you with the Angular CLI. Jasmine is also dependency free and doesn’t require a DOM.

Jasmine是一个行为驱动的开发框架,用于测试与Karma配合良好JavaScript代码。 与Karma相似,它也是Angular文档中推荐的测试框架,因为它是使用Angular CLI为您设置的。 Jasmine也是不受依赖的,并且不需要DOM。

As far as features go, I love that Jasmine has almost everything I need for testing built into it. The most notable example would be spies. A spy allows us to “spy” on a function and track attributes about it such as whether or not it was called, how many times it was called, and with which arguments it was called. With a framework like Mocha, spies are not built-in and would require pairing it with a separate library like Sinon.js.

就功能而言,我喜欢Jasmine内置了几乎所有测试所需的功能。 最著名的例子是间谍。 间谍使我们可以“监视”一个函数并跟踪有关它的属性,例如是否调用了该函数,调用了多少次以及调用了哪些参数。 使用Mocha之类的框架,间谍不是内置的,需要将其与Sinon.js之类的单独库配对。

The good news is that the switching costs between testing frameworks is relatively low with differences in syntax as small as Jasmine's toEqual() and Mocha's to.equal().

好消息是,测试框架之间的转换成本相对较低,语法差异小至Jasmine的toEqual()和Mocha的to.equal()

一个简单的测试示例 ( A Simple Test Example )

Imagine you had an alien servant named Adder who follows you everywhere you go. Other than being a cute alien companion Adder can really only do one thing, add two numbers together.

想象一下,您有一个名为Adder的外星仆人,他随处可见。 除了成为可爱的外星人伴侣,Adder真的只能做一件事,将两个数字加在一起。

To verify Adder's ability to add two numbers we could generate a set of test cases to see if Adder provides us the correct answer.

为了验证Adder能够将两个数字相加的能力,我们可以生成一组测试用例,以查看Adder是否为我们提供了正确的答案。

Within Jasmine, this would begin with what's referred to as a "suite" which groups a related set of tests by calling the function describe.

在Jasmine中,这将从所谓的“套件”开始,该套件通过调用函数describe来对一组相关的测试进行分组。

// A Jasmine suite
describe('Adder', () => {

});

From here we could provide Adder with a set of test cases such as two positive numbers (2, 4), a positive number and a zero (3, 0), a positive number and a negative number (5, -2), and so on.

从这里我们可以为Adder提供一组测试用例,例如两个正数(2,4),一个正数和一个零(3,0),一个正数和一个负数(5,-2),以及以此类推。

Within Jasmine, these are referred to as "specs" which we create by calling the function it, passing it a string to describe the functionality that's being tested.

在Jasmine中,这些被称为“规范” ,我们通过调用it的函数,将其传递给一个字符串来描述正在测试的功能来创建。

describe('Adder', () => {
  // A jasmine spec
  it('should be able to add two whole numbers', () => {
    expect(Adder.add(2, 2)).toEqual(4);
  });

  it('should be able to add a whole number and a negative number', () => {
    expect(Adder.add(2, -1)).toEqual(1);
  });

  it('should be able to add a whole number and a zero', () => {
    expect(Adder.add(2, 0)).toEqual(2);
  });
});

Within each spec we call expect and provide it what is referred to as an "actual"—the call site of our actual code. After the expectation, or expect, is the chained "matcher" function, such as toEqual, which the testing developer provides with the expected output of the code being tested.

在每个规范中,我们将expect称为“ expect并将其提供给“实际”,即我们实际代码的调用位置。 预期后,或expect ,是链接的“匹配”功能,诸如toEqual ,其检测显影剂的代码的预期输出被测试提供。

There are many other matchers available to us other than toEqual. You can see a full list within Jasmine's documentation.

除了toEqual之外,我们还有许多其他匹配器可用。 您可以在Jasmine的文档中看到完整列表。

Our tests aren't concerned with how Adder arrives at the answer. We only care about the answer Adder provides us. For all we know, this may be Adder's implementation of add.

我们的测试与Adder 如何得出答案无关。 我们只关心Adder为我们提供的答案。 就我们所知,这可能是Adder的add的实现。

function add(first, second) {
  if (true) { // why?
    if (true) { // why??
      if (1 === 1) { // why?!?1
        return first + second;
      }
    }
  }
}

In other words, we only care that Adder behaves as expected—we have no concern for Adder's implementation.

换句话说,我们只关心Adder的行为是否符合预期—我们不关心Adder的实现。

This is what makes a practice like test-driven development (TDD) so powerful. You can first write a test for a function and its expected behavior and get it to pass. Then, once it's passing, you can refactor your function to a different implementation and if the test is still passing, you know your function is still behaving as specified within your tests even with a different implementation. Adder's add function would be a good example!

这就是使像测试驱动开发(TDD)这样的实践如此强大的原因。 您可以首先为功能及其预期行为编写测试,然后使其通过。 然后,一旦通过,您就可以将函数重构为其他实现,并且如果测试仍在通过,则您知道即使使用其他实现,您的函数仍会按照测试中指定的方式运行。 加法器的add函数将是一个很好的例子!

角度设置 ( Angular setup )

We'll begin by creating our new application using the Angular CLI.

我们将从使用Angular CLI创建新应用程序开始。

ng new angular-testing --routing

Since we'll have multiple views in this application we use the --routing flag so the CLI automatically generates a routing module for us.

由于在此应用程序中将有多个视图,因此我们使用--routing标志,因此CLI会自动为我们生成一个路由模块。

From here we can verify everything is working correctly by moving into the new angular-testing directory and running the application.

在这里,我们可以移至新的angular-testing目录并运行该应用程序,以验证一切工作正常。

cd angular-testing
ng serve -o

You can also verify the application's tests are currently in a passing state.

您还可以验证应用程序的测试当前处于通过状态。

ngtest

添加主页 ( Adding a home page )

Before creating a service to populate our home page with users, we'll start by creating the home page.

在创建用于向用户填充我们的主页的服务之前,我们将从创建主页开始。

ng g component home

Now that our component has been created, we can update our routing module's (app-routing.module.ts) root path to HomeComponent.

现在已经创建了组件,我们可以将路由模块的( app-routing.module.ts )根路径更新为HomeComponent

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';

const routes: Routes = [
  { path: '', component: HomeComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Run the application if it isn't already and you should now see "home works!" below the default template in app.component.html which was created by the CLI.

如果尚未运行该应用程序,则现在应该看到“家庭作品!” 在CLI所创建的app.component.html中的默认模板下面。

删除AppComponent测试 ( Removing AppComponent tests )

Since we no longer need the default contents of AppComponent, let's update it by removing some unnecessary code.

由于我们不再需要AppComponent的默认内容, AppComponent我们通过删除一些不必要的代码来对其进行更新。

First, remove everything in app.component.html so that only the router-outlet directive remains.

首先,删除app.component.html中的所有内容,以便仅保留router-outlet指令。

<router-outlet></router-outlet>

Within app.component.ts, you can also remove the title property.

app.component.ts ,您还可以删除title属性。

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent { }

Finally, you can update the tests in app.component.spec.ts by removing the two tests for some of the contents that were previously in app.component.html.

最后,您可以更新测试app.component.spec.ts通过移除两个测试对于一些在以前的内容app.component.html

import { async, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));
  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));
});

测试Angular服务 ( Testing an Angular service )

Now that our home page is set up we can work on creating a service to populate this page with our directory of employees.

现在我们的主页已经建立,我们可以创建一个服务来用我们的员工目录填充此页面。

ng gservice services/users/users

Here we've created our users service within a new services/users directory to keep our services away from the default app directory which can get messy quick.

在这里,我们在新的services/users目录中创建了users服务,以使我们的服务远离默认的app目录,因为该目录可能会很快变得混乱。

Now that our service is created, we can make a few small changes to the test file services/users/users.service.spec.ts.

现在,我们的服务已创建,我们可以对测试文件services/users/users.service.spec.ts进行一些小的更改。

I personally find injecting dependencies within it() to be a bit repetitive and harder to read as it's done in the default scaffolding for our test file as shown below:

我个人发现在it()注入依赖项有些重复并且更难阅读,因为它在测试文件的默认框架中完成,如下所示:

it('should be created', inject([TestService], (service: TestService) => {
  expect(service).toBeTruthy();
}));

With a few minor changes, we can move this into the beforeEach removing the duplication from each it.

有一些小的变化,我们可以将这个进入beforeEach从每个移除重复it

import { TestBed } from '@angular/core/testing';
import { UsersService } from './users.service';

describe('UsersService', () => {
  let usersService: UsersService; // Add this

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [UsersService]
    });

    usersService = TestBed.get(UsersService); // Add this
  });

  it('should be created', () => { // Remove inject()
    expect(usersService).toBeTruthy();
  });
});

In the code above, TestBed.configureTestingModule({}) sets up the service we want to test with UsersService set in providers. We then inject the service into our test suite using TestBed.get() with the service we want to test as the argument. We set the return value to our local usersService variable which will allow us to interact with this service within our tests just as we would within a component.

在上面的代码中, TestBed.configureTestingModule({})设置了我们要使用UsersService设置的UsersService进行测试的providers 。 然后,我们使用TestBed.get()将服务注入到我们的测试套件中, TestBed.get()要测试的服务作为参数。 我们将返回值设置为本地usersService变量,这将使我们能够像在组件内部一样在测试中与该服务进行交互。

Now that our test setup is restructured, we can add a test for an all method which will return a collection of users.

现在,我们的测试设置已经重组,我们可以为all方法添加一个测试,该测试将返回用户集合。

import { of } from 'rxjs'; // Add import

describe('UsersService', () => {
  ...

  it('should be created', () => {
    expect(usersService).toBeTruthy();
  });

  // Add tests for all() method
  describe('all', () => {
    it('should return a collection of users', () => {
      const userResponse = [
        {
          id: '1',
          name: 'Jane',
          role: 'Designer',
          pokemon: 'Blastoise'
        },
        {
          id: '2',
          name: 'Bob',
          role: 'Developer',
          pokemon: 'Charizard'
        }
      ];
      let response;
      spyOn(usersService, 'all').and.returnValue(of(userResponse));

      usersService.all().subscribe(res => {
        response = res;
      });

      expect(response).toEqual(userResponse);
    });
  });
});

Here we add a test for the expectation that all will return a collection of users. We declare a userResponse variable set to a mocked response of our service method. Then we use the spyOn() method to spy on usersService.all and chain .returnValue() to return our mocked userResponse variable wrapping it with of() to return this value as an observable.

在这里,我们添加了一个测试,期望all用户将返回用户集合。 我们声明一个userResponse变量设置为我们的服务方法的userResponse响应。 然后我们使用spyOn()方法来刺探usersService.all和链.returnValue()返回我们的嘲笑userResponse可变包裹它of()返回这个值作为可观察到的。

With our spy set, we call our service method as we would within a component, subscribe to the observable, and set its return value to response.

使用我们的间谍集,我们就像在组件内那样调用服务方法,订阅可观察对象,并将其返回值设置为response

Finally, we add our expectation that response will be set to the return value of the service call, userResponse.

最后,我们期望将response设置为服务调用的返回值userResponse

为什么要嘲笑? ( Why mock? )

At this point many people ask, "Why are we mocking the response?" Why did we provide our test a return value userResponse that we created ourselves, to manually set what’s being returned from our service? Shouldn’t the service call return the real response from the service, whether it's a hard-coded set of users or a response from an HTTP request?

在这一点上,许多人问:“我们为什么要嘲笑回应?” 为什么我们userResponse测试提供一个我们自己创建的返回值userResponse ,以手动设置服务返回的值? 服务调用是否应该返回服务的真实响应,无论是用户的硬编码集还是来自HTTP请求的响应?

This is a perfectly reasonable question to ask and one that can be hard to wrap your head around when you first begin testing. I find this concept is easiest to illustrate with a real world example.

这是一个完全合理的问题,当您开始进行测试时可能很难缠住头。 我发现用一个真实的例子最容易说明这个概念。

Imagine you own a restaurant and it’s the night before opening day. You gather everyone you’ve hired for a “test run” of the restaurant. You invite a few friends to come in and pretend they’re customers who will sit down and order a meal.

想象一下,您拥有一家餐厅,那是营业日的前一天晚上。 您聚集了为餐厅“试运行”而雇用的所有人。 您邀请几个朋友进来,假装他们是坐下来订餐的顾客。

No dishes will actually be served in your test run. You’ve already worked with your cooks and are satisfied they can make the dishes correctly. In this test run you want to test the transition from the customer ordering their dish, to the waiter sending that to the kitchen, and then the waiters fulfilling the kitchen’s response to the customer. This response from the kitchen may be one of a few options.

在您的测试运行中,实际上不会提供任何菜肴。 您已经与您的厨师一起工作过,并对他们可以正确制作菜肴感到满意。 在此测试运行中,您要测试从客户订购菜品到服务员将菜品发送到厨房,然后由服务员完成厨房对客户的响应的过渡。 厨房的这种回应可能是少数选择之一。

  1. The meal is ready.

    饭准备好了
  2. The meal is delayed.

    吃饭延迟了。
  3. The meal cannot be made. We ran out of ingredients for the dish.

    不能做饭。 我们的菜用完了。

If the meal is ready, the waiter delivers the meal to the customer. However, in the event that a meal is late or cannot be made, the waiter will have to go back to the customer, apologize, and potentially ask for a second dish.

如果餐点准备好了,服务员将餐点交付给顾客。 但是,如果晚饭或无法做饭,服务生将不得不回头给顾客,道歉,并有可能要求第二道菜。

In our test run, it wouldn’t make sense to actually create the meals when we want to test the front-end’s (waiter’s) ability to fulfill the requests received from the backend (kitchen). More importantly, if we wanted to test our waiters could actually apologize to customers in the cases where a meal is delayed or cannot be made we would literally be waiting until our cooks were too slow or we ran out of ingredients before our tests for those cases could be confirmed. For this reason, we would “mock” the backend (kitchen) and give the waiters whatever scenario it is that we want to test.

在我们的测试运行中,当我们想要测试前端(服务员)满足从后端(厨房)收到的请求的能力时,实际创建餐点没有任何意义。 更重要的是,如果我们想测试我们的服务生可能在用餐延迟或无法做饭的情况下向顾客道歉,我们将等到厨师做得太慢或在测试之前用尽食材。可以确认。 因此,我们将“模拟”后端(厨房),并为服务员提供我们要测试的任何情况。

Similarly in code, we don’t actually hit the API when we’re testing various scenarios. We mock the response we may expect to receive and verify that our application can handle that response accordingly. Just like our kitchen example, if we were testing our application’s ability to handle an API call that failed we would literally have to wait for our API to fail to verify it could handle that case—a scenario that hopefully won’t be happening that often!

同样,在代码中,我们在测试各种场景时实际上并没有使用API​​。 我们模拟可能期望收到的响应,并验证我们的应用程序可以相应地处理该响应。 就像我们的厨房示例一样,如果我们正在测试应用程序处理失败的API调用的能力,那么我们实际上必须等待我们的API未能验证它可以处理这种情况-希望这种情况不会经常发生!

添加用户 ( Adding users )

To get this test to pass, we need to implement the service method in users.service.ts.

为了使此测试通过,我们需要在users.service.ts实现service方法。

First, we'll start by adding our imports and a collection of employees to the service.

首先,我们将在服务中添加我们的进口商品和一批员工。

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs'; // Add imports

@Injectable({
  providedIn: 'root'
})
export class UsersService {
  users: Array<object> = [  // Add employee object
    {
      id: '1',
      name: 'Jane',
      role: 'Designer',
      pokemon: 'Blastoise'
    },
    {
      id: '2',
      name: 'Bob',
      role: 'Developer',
      pokemon: 'Charizard'
    },
    {
      id: '3',
      name: 'Jim',
      role: 'Developer',
      pokemon: 'Venusaur'
    },
    {
      id: '4',
      name: 'Adam',
      role: 'Designer',
      pokemon: 'Yoshi'
    }
  ];

  constructor() { }
}

Then, just below our constructor, we can implement all.

然后,在构造函数下方,我们可以实现all

all(): Observable<Array<object>> {
  return of(this.users);
}

Run ng test again and you should now have passing tests including the new tests for our service method.

再次运行ng test ,您现在应该已经通过了测试,包括针对我们的服务方法的新测试。

将用户添加到主页 ( Add users to the home page )

Now that our service method is ready to use, we can work towards populating our home page with these users.

现在可以使用我们的服务方法了,我们可以努力向这些用户填充主页。

First, we'll update index.html with Bulma to help us with some styling.

首先,我们将使用Bulma更新index.html ,以帮助我们进行一些样式设置。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AngularTesting</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <!--Add these-->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>
</head>
<body>
  <app-root></app-root>
</body>
</html>

Then within home/home.component.ts we can add a call to our new service.

然后,我们可以在home/home.component.ts中添加对新服务的调用。

import { Component, OnInit } from '@angular/core';
import { UsersService } from '../services/users/users.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  users;

  constructor(private usersService: UsersService) { }

  ngOnInit() {
    this.usersService.all().subscribe(res => {
      this.users = res;
    });
  }

}

First we import our service and inject it into our component's constructor. Then we add a call to the service method within ngOnInit and set the return value to our component's users property.

首先,我们导入服务并将其注入到组件的构造函数中。 然后,在ngOnInit添加对service方法的ngOnInit ,并将返回值设置为组件的users属性。

To display these users to the view, update the template in home/home.component.html.

要在视图中显示这些用户,请更新home/home.component.html的模板。

<section class="section is-small">
  <div class="container">
    <div class="columns">
      <div class="column" *ngFor="let user of users">
        <div class="box">
          <div class="content">
            <p class="has-text-centered is-size-5">{% raw %}{{user.name}}{% endraw %}</p>
            <ul>
              <li><strong>Role:</strong> {% raw %}{{user.role}}{% endraw %}</li>
              <li><strong>Pokemon:</strong> {% raw %}{{user.pokemon}}{% endraw %}</li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

Now when you run ng serve and view the home page, you should see the users displayed within Bulma boxes.

现在,当您运行ng serve并查看主页时,您应该看到在布尔玛框中显示的用户。

查找单个用户 ( Finding a single user )

Now that our users are being populated into our home page, we'll add one more service method for finding a single user that will be used for the user profile pages.

现在,我们的用户已填充到我们的主页中,我们将添加另一种服务方法来查找将用于用户个人资料页面的单个用户。

First we'll add the tests for our new service method.

首先,我们将为新的服务方法添加测试。

describe('all', () => {
  ...
});

describe('findOne', () => {
  it('should return a single user', () => {
    const userResponse = {
      id: '2',
      name: 'Bob',
      role: 'Developer',
      pokemon: 'Charizard'
    };
    let response;
    spyOn(usersService, 'findOne').and.returnValue(of(userResponse));

    usersService.findOne('2').subscribe(res => {
      response = res;
    });

    expect(response).toEqual(userResponse);
  });
});

Here we add a test for the expectation that findOne will return a single user. We declare a userResponse variable set to a mocked response of our service method, a single object from the collection of users.

在这里,我们添加了一个测试,以期望findOne将返回单个用户。 我们声明一个userResponse变量,该变量设置为我们的服务方法的userResponse响应,这是用户集合中的单个对象。

We then create a spy for usersService.findOne and return our mocked userResponse variable. With our spy set, we call our service method and set its return value to response.

然后,我们为usersService.findOne创建一个间谍,并返回userResponse变量。 设置好间谍之后,我们将调用服务方法并将其返回值设置为response

Finally, we add our assertion that response will be set to the return value of the service call, userResponse.

最后,我们添加断言,即将response设置为服务调用的返回值userResponse

To get this test to pass, we can add the following implementation to users.service.ts.

为了使此测试通过,我们可以将以下实现添加到users.service.ts

all(): Observable<Array<object>> {
  return of(this.users);
}

findOne(id: string): Observable<object> {
  const user = this.users.find((u: any) => {
    return u.id === id;
  });
  return of(user);
}

Now when you run ng test you should see all of the tests in a passing state.

现在,当您运行ng test您应该看到所有测试都处于通过状态。

结论 ( Conclusion )

At this point I hope testing, both its benefits and the reason for writing them, is starting to become a bit more clear. Personally, I pushed off testing for the longest time and my reasons were primarily because I didn't understand the why behind them and resources for testing were limited.

在这一点上,我希望测试的好处和编写它们的理由,已经变得更加清晰。 就我个人而言,我推迟了最长的测试时间,原因主要是因为我不了解其背后的原因以及测试资源有限。

What we've created in this tutorial isn't the most visually impressive application but it's a step in the right direction.

我们在本教程中创建的内容并不是视觉上最令人印象深刻的应用程序,而是朝着正确方向迈出的一步。

In the next tutorial, we'll create the user profile page and a service to retrieve a Pokemon image using the Pokeapi. We'll learn how to test service methods that make HTTP requests and how to test components.

在下一个教程中,我们将创建用户个人资料页面和使用Pokeapi检索Pokemon图像的服务 。 我们将学习如何测试发出HTTP请求的服务方法以及如何测试组件。

额外 ( Extra )

If you want the tests to display in a more readable format within your terminal, there's an npm package for this.

如果您希望测试在终端中以更易读的格式显示,则有一个npm软件包。

First, install the package.

首先,安装软件包。

npm install karma-spec-reporter --save-dev

Once that's finished, open src/karma.conf.js, add the new package to plugins, and update the progress value within reporters to spec.

完成后,打开src/karma.conf.js ,将新包添加到plugins ,然后将reportersprogress值更新为spec

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma'),
      require('karma-spec-reporter') // Add this
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../coverage'),
      reports: ['html', 'lcovonly'],
      fixWebpackSourcePaths: true
    },
    reporters: ['spec', 'kjhtml'], // Update progress to spec
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};

Now when you run ng test you should have a more visually appealing output for your test suite.

现在,当您运行ng test您应该为测试套件提供更具视觉吸引力的输出。

翻译自: https://scotch.io/tutorials/testing-angular-with-jasmine-and-karma-part-1

karma jasmine

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值