---
description: >-
  https://helpex.vn/article/cach-kiem-tra-thanh-phan-phan-ung-voi-jest-va-enzyme-phan-2-5c54de20507419248c9b0128
---

# Cách kiểm tra thành phần phản ứng với Jest và Enzyme, Phần 2

Chào mừng trở lại! Nếu bạn bỏ lỡ Phần 1, bạn có thể xem tại đây !

### Ví dụ về kiểm tra

Hãy chuyển sang các ví dụ và bao gồm các thành phần với các thử nghiệm được mô tả trong bài viết trước, từng bước một.

#### 1. Kiểm tra một thành phần từ các hình thức / đầu vào

Lấy một thành phần từ thư mục Forms / input; hãy để nó là DateInput.js, thành phần cho trường datepicker.

**Danh sách mã cho thành phần được thử nghiệm:**  [**DateInput.js**](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/forms/inputs/DateInput.js)

**Giống như:**

![Cách kiểm tra thành phần phản ứng với Jest và Enzyme, Phần 2](https://cdn.helpex.vn/upload/2019/2/2/ar/07-02-36-834-8e924c60-4873-43a0-ace4-6542555cecc6.jpg)

Thành  `DateInput` phần này sử dụng công cụ `valueToDate` hẹn giờ phản ứng với hai tiện ích:  (chuyển đổi giá trị thành ngày) và  `dateToValue` ngược lại, đó là gói thời điểm để thao tác ngày và PropTypes để kiểm tra đạo cụ React.

Theo mã thành phần, chúng ta có thể thấy danh sách các đạo cụ mặc định - và, với sự trợ giúp mà thành phần sẽ được hiển thị:

```
const defaultProps = {      inputClassName: 'input-custom',    monthsShown: 1,    dateFormat: 'DD.MM.YYYY',    showMonthYearsDropdowns: false,    minDate: moment()};
```

Tất cả các đạo cụ đều phù hợp để tạo ảnh chụp nhanh, ngoại trừ một:  `minDate: moment()` -  `moment()` sẽ cung cấp cho chúng tôi ngày hiện tại mỗi lần chúng tôi chạy thử nghiệm và ảnh chụp nhanh sẽ thất bại vì nó lưu trữ một ngày lỗi thời. Một giải pháp là giả định giá trị này:

```
const defaultProps = {      minDate: moment(0)}
```

Tôi cần chỗ dựa này  `minDate` trong mỗi thành phần được kết xuất; để tránh trùng lặp đạo cụ, tôi tạo HOC nhận  `defaultProps` và trả về một thành phần đẹp:

```
import TestDateInput from '../DateInput';  const DateInput = (props) =>      <testdateinput {...defaultprops}="" {...props}=""></testdateinput>;
```

Đừng quên  `moment-timezone`, đặc biệt là nếu các bài kiểm tra của bạn sẽ được điều hành bởi các nhà phát triển từ một quốc gia khác ở múi giờ khác. Họ sẽ nhận được một giá trị giả, nhưng với sự thay đổi múi giờ. Giải pháp là đặt múi giờ mặc định:

```
const moment = require.requireActual('moment-timezone').tz.setDefault('America/Los_Angeles')  
```

Bây giờ, thành phần đầu vào ngày đã sẵn sàng để thử nghiệm.

**1. Tạo ảnh chụp nhanh trước:**

```
it('render correctly date component', () => {      const DateInputComponent = renderer.create(<dateinput></dateinput>).toJSON();    expect(DateInputComponent).toMatchSnapshot();});
```

**2. Đạo cụ kiểm tra:**

Dự phòng đầu tiên để kiểm tra là  `showMonthYearsDropdowns` và nếu nó được đặt thành đúng, thì việc thả xuống theo tháng và năm được hiển thị:

```
it('check month and years dropdowns displayed', () => {      const props = {            showMonthYearsDropdowns: true        },        DateInputComponent = mount(<dateinput {...props}=""></dateinput>).find('.datepicker');    expect(DateInputComponent.hasClass('react-datepicker-hide-month')).toEqual(true);});
```

Kiểm tra giá trị prop null; kiểm tra này là cần thiết để đảm bảo thành phần được hiển thị mà không có giá trị xác định:

```
it('render date input correctly with null value', () => {      const props = {            value: null        },        DateInputComponent = mount(<dateinput {...props}=""></dateinput>);    expect((DateInputComponent).prop('value')).toEqual(null);});
```

**3. Kiểm tra các nguyên mẫu cho giá trị - ngày dự kiến ​​là một chuỗi:**

```
it('check the type of value', () => {      const props = {            value: '10.03.2018'        },        DateInputComponent = mount(<dateinput {...props}=""></dateinput>);    expect(DateInputComponent.prop('value')).toBeString();});
```

**4. Sự kiện thử nghiệm:**

**4.1.** Kiểm tra sự kiện onChange, để biết giả lập cuộc gọi lại đó => kết xuất thành phần đầu vào ngày => sau đó mô phỏng sự kiện thay đổi với giá trị đích mới => và cuối cùng kiểm tra xem sự kiện onChange đã được gọi với giá trị mới chưa.

```
it('check the onChange callback', () => {      const onChange = jest.fn(),        props = {            value: '20.01.2018',            onChange        },        DateInputComponent = mount(<dateinput {...props}=""></dateinput>).find('input');    DateInputComponent.simulate('change', { target: {value: moment('2018-01-22')} });    expect(onChange).toHaveBeenCalledWith('22.01.2018');});
```

**4.2.** Đảm bảo cửa sổ bật lên datepicker mở sau khi nhấp vào đầu vào ngày. Đối với điều đó, tìm ngày nhập = = mô phỏng sự kiện nhấp => và mong đợi cửa sổ bật lên với lớp  `.react-datepicker` hiện tại.

```
it('check DatePicker popup open', () => {      const DateComponent = mount(<dateinput></dateinput>),        dateInput = DateComponent.find("input[type='text']");    dateInput.simulate('click');    expect(DateComponent.find('.react-datepicker')).toHaveLength(1);});
```

**Mã kiểm tra đầy đủ:**  [DateInput.test.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/forms/inputs/__tests__/DateInput.test.js)

### 2. Kiểm tra tiện ích

**Mã cho tiện ích được thử nghiệm:**  [valueToDate.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/utils/valueToDate.js)

Mục đích của tiện ích này là biến đổi giá trị thành một ngày với định dạng tùy chỉnh. Trước hết, hãy phân tích tiện ích đã cho và xác định các trường hợp chính để thử nghiệm:

**1.** Theo mục đích của tiện ích này, nó biến đổi giá trị, vì vậy chúng ta cần kiểm tra giá trị này:

* Trong trường hợp giá trị không được xác định, chúng tôi cần chắc chắn rằng tiện ích sẽ không trả lại ngoại lệ (lỗi).
* Trong trường hợp giá trị được xác định, chúng ta cần kiểm tra xem tiện ích có trả về ngày không.

**2.** Giá trị trả về phải thuộc về  `moment` lớp; đó là lý do tại sao nó phải là một thể hiện của  `moment`.

**3.** Đối số thứ hai là  `dateFormat`; đặt nó là một hằng số trước khi kiểm tra. Đó là lý do tại sao nó sẽ được thông qua trong mỗi thử nghiệm và trả về một giá trị theo định dạng ngày. Có nên thử nghiệm  `dateFormat` riêng? Tôi cho là không. Đối số này là tùy chọn; nếu chúng ta không thiết lập  `dateFormat`, tiện ích sẽ không bị hỏng và nó sẽ chỉ trả về một ngày ở định dạng mặc định; đó là một công việc nhất thời, chúng ta không nên kiểm tra các thư viện của bên thứ ba. Như tôi đã đề cập trước đây, chúng ta không nên quên về múi giờ; đó là một điểm rất quan trọng, đặc biệt đối với các nhà phát triển từ các múi giờ khác nhau.

**Hãy để mã:**

**1. Viết một bài kiểm tra cho trường hợp đầu tiên; Khi chúng ta không có giá trị, nó trống rỗng.**

```
const format = 'DD.MM.YYYY'; it('render valueToDate utility with empty value', () => {      const value = valueToDate('', format);    expect(value).toEqual(null);});
```

**2. Kiểm tra xem giá trị được xác định.**

```
const date = '21.11.2015',        format = ‘DD.MM.YYYY’; it('render valueToDate utility with defined value', () => {      const value = valueToDate(date, format);    expect(value).toEqual(moment(date, format));});
```

**3. Kiểm tra xem giá trị thuộc về lớp khoảnh khắc.**

```
const date = '21.11.2015',      format = 'DD.MM.YYYY'; it('check value is instanceof moment', () => {      const value = valueToDate(date, format);    expect(value instanceof moment).toBeTruthy();});
```

**Mã kiểm tra đầy đủ:**  [valueToDate.test.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/utils/__tests__/valueToDate.test.js)

### 3. Kiểm tra Widgets

Để kiểm tra widget, tôi đã sử dụng thành phần spinner.

**Danh sách mã cho widget đã kiểm tra:**  [Spinner.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/widgets/Spinner.js)

**Giống như:**

![Cách kiểm tra thành phần phản ứng với Jest và Enzyme, Phần 2](https://cdn.helpex.vn/upload/2019/2/2/ar/07-02-39-803-24110275-3b73-4a29-90ed-ec89a5a7ad81.jpg)

Spinner không yêu cầu giải thích, vì hầu như tất cả các tài nguyên web đều có thành phần này. \
Vì vậy, hãy để viết bài kiểm tra:

**1. Bước đầu tiên - tạo ảnh chụp nhanh:**

```
it('render correctly Spinner component', () => {     const SpinnerComponent = mount(<spinner></spinner>);   expect(SpinnerComponent).toMatchSnapshot();});
```

**2. Đạo cụ kiểm tra:**

**2.1**  Tiêu đề chống đỡ mặc định, kiểm tra xem nó có hiển thị chính xác không.

```
it('check prop title by default', () => {   const SpinnerComponent = mount(<spinner></spinner>);    expect(SpinnerComponent.find('p').text()).toEqual('Please wait');});
```

**2.2**  Kiểm tra tiêu đề chống đỡ tùy chỉnh; Tôi cần kiểm tra xem nó có trả về một prop được xác định chính xác không. Hãy xem mã, tiêu đề được bọc trong tiện  `rawMarkup` ích và đầu ra với sự trợ giúp của  `dangerouslySetInnerHTML` tài sản.

Danh sách mã cho các tiện   `rawMarkup` ích:

```
export default function rawMarkup(template) {      return {__html: template};}
```

Chúng ta có cần bao gồm các thử nghiệm cho  `rawMarkup` trong thành phần spinner không? Không, nó là một tiện ích riêng biệt và nó nên được kiểm tra ngoài spinner. Chúng tôi không quan tâm làm thế nào nó hoạt động; chúng ta chỉ cần biết rằng prop prop trả về kết quả chính xác.

**Làm rõ** : Lý do sử dụng  `dangerouslySetInnerHTML` tài sản là như sau: trang web của chúng tôi là đa ngôn ngữ, do đó một nhóm tiếp thị dịch thuật chịu trách nhiệm. Họ có thể dịch một cách đơn giản với sự kết hợp của từ hoặc thậm chí trang trí nó với các thẻ HTML, giống như  ,  ,   hoặc thậm chí lát văn bản với danh sách&#x20;

1.

,&#x20;

*

; chúng tôi không biết chắc chắn cách họ dịch và trang trí văn bản. Chúng ta chỉ cần kết xuất chính xác tất cả những thứ này.

Tôi đã kết hợp hai trường hợp thử nghiệm chính thành một thử nghiệm:

* trả lại đúng tiêu đề prop
* hiển thị chính xác tiêu đề prop với các thẻ HTML

```
it('check prop title with html tags', () => {      const props = {            title: '<b>Please wait</b>'        },        SpinnerComponent = mount(<spinner {...props}=""></spinner>);    expect(SpinnerComponent.find('p').text()).toEqual('Please wait');});
```

Lấy chỗ dựa tiếp theo ,  `subTitle`; nó là tùy chọn và đó là lý do tại sao nó không có chỗ dựa mặc định, vì vậy hãy bỏ qua bước này với đạo cụ mặc định và kiểm tra đạo cụ tùy chỉnh:

* Kiểm tra xem văn bản trong  `subTitle` prop hiển thị chính xác:

```
const props = {          subTitle: 'left 1 minute'    },    SpinnerComponent = mount(<spinner {...props}=""></spinner>); it('render correct text', () => {      expect(SpinnerComponent.find('p').at(1).text()).toEqual(props.subTitle);});
```

Chúng tôi biết rằng đó  `subTitle` là tùy chọn; đó là lý do tại sao chúng ta cần kiểm tra xem nó không được hiển thị với các đạo cụ mặc định hay không, theo đánh dấu cắt lát. Chỉ cần kiểm tra số lượng thẻ&#x20;

:

```
it('check subTitle is not rendered', () => {    const SpinnerComponent = mount(<spinner></spinner>);    expect(SpinnerComponent.find('p').length).toEqual(1);});
```

**3. Thử nghiệm các loại prop:**

* Đối với đạo cụ tiêu đề, chúng tôi hy vọng nó là một chuỗi:

```
it('check prop type for title is string', () => {      const props = {            title: 'Wait'        },        SpinnerComponent = mount(<spinner {...props}=""></spinner>);    expect(SpinnerComponent.find('p').text()).toBeString();});
```

* Đối với  `subTitle` đạo cụ, chúng tôi cũng dự kiến ​​nó sẽ là một chuỗi:

```
const props = {          subTitle: 'left 1 minute'    },    SpinnerComponent = mount(<spinner {...props}=""></spinner>); it('type for subTitle is string', () => {      expect(SpinnerComponent.find('p').at(1).text()).toBeString();});
```

**Danh sách kiểm tra đầy đủ:**  [Spinner.test.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/widgets/__tests__/Spinner.test.js)

**4. Kiểm tra phương thức (ModalWrapper.js và ModalTrigger.js)**

_**Giống như:**_![Cách kiểm tra thành phần phản ứng với Jest và Enzyme, Phần 2](https://cdn.helpex.vn/upload/2019/2/2/ar/07-02-40-394-ba596e3e-2048-407f-b3cd-1f2065313f74.jpg)

Cách kiểm tra phương thức.

Trước hết, tôi muốn giải thích cách thức các phương thức được tổ chức trong dự án của chúng tôi. Chúng tôi có hai thành phần:  **ModalWrapper.js**  và  **ModalTrigger.js** .

**ModalWrapper**  chịu trách nhiệm bố trí cửa sổ bật lên. Nó chứa thùng chứa phương thức, nút 'đóng', tiêu đề phương thức và thân máy.

**ModalTrigger**  chịu trách nhiệm xử lý phương thức. Nó bao gồm bố trí ModalWrapper và chứa các sự kiện để kiểm soát bố cục của phương thức (các hành động mở, đóng).

Chúng ta hãy đi qua từng thành phần riêng biệt:

**1. Danh sách mã cho thành phần được thử nghiệm:** [ModalWrapper.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/modals/ModalWrapper.js)

**Hãy để mã:**

**1.1**  ModalWrapper nhận được một thành phần và kết xuất nó bên trong. Trước hết, hãy kiểm tra xem ModalWrapper sẽ không bị lỗi nếu không có thành phần. Tạo ảnh chụp nhanh với các đạo cụ mặc định:

```
it('without component', () => {      const ModalWrapperComponent = shallow(<modalwrapper></modalwrapper>);    expect(ModalWrapperComponent).toMatchSnapshot();});
```

**1.2**  Bước tiếp theo là mô phỏng các điều kiện thực tế của nó với kết xuất thành phần được truyền qua các đạo cụ:

```
it('with component', () => {     const props = {           component: () => {}        },        ModalWrapperComponent = shallow(<modalwrapper {...props}=""></modalwrapper>);    expect(ModalWrapperComponent).toMatchSnapshot();});
```

**1.3**  Đạo cụ kiểm tra:

Nhận một tên lớp tùy chỉnh prop:

```
it('render correct class name', () => {      const props = {            modalClassName: 'custom-class-name'        },        ModalWrapperComponent = shallow(<modalwrapper {...props}=""></modalwrapper>).find('Modal');        expect(ModalWrapperComponent.hasClass('custom-class-name')).toEqual(true);});
```

Nhận một prop tiêu đề tùy chỉnh:

```
it('render correct title', () => {      const props = {           title: 'Modal Title'       },       ModalWrapperComponent = shallow(<modalwrapper {...props}=""></modalwrapper>).find('ModalTitle');    expect(ModalWrapperComponent.props().children).toEqual('Modal Title');});
```

Nhận được một prop hiển thị chính xác:

```
 it('check prop value', () => {        const props = {               show: true           },           ModalWrapperComponent = shallow(<modalwrapper {...props}=""></modalwrapper>).find('Modal');        expect(ModalWrapperComponent.props().show).toEqual(true);    });
```

**1.4**  Các loại prop thử nghiệm:

* cho chương trình chống đỡ

```
it('check prop type', () => {      const props = {           show: true        },        ModalWrapperComponent = shallow(<modalwrapper {...props}=""></modalwrapper>).find('Modal');    expect(ModalWrapperComponent.props().show).toBeBoolean();});
```

* cho chỗ dựa

```
it('render correct onHide prop type', () => {      const props = {            onHide: () => {}        },        ModalWrapperComponent = shallow(<modalwrapper {...props}=""></modalwrapper>).find('Modal');    expect(ModalWrapperComponent.props().onHide).toBeFunction();});
```

* cho thành phần chống đỡ

```
it(‘render correct component prop type’, () => {     const props = {           component: () => {}       },       ModalWrapperComponent = mount(<modalwrapper {...props}=""></modalwrapper>);   expect(ModalWrapperComponent.props().component).toBeFunction();});
```

**Danh sách kiểm tra đầy đủ:**  [ModalWrapper.test.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/modals/__tests__/ModalWrapper.test.js)

**2. Danh sách mã cho một thành phần được thử nghiệm:**  [ModalTrigger.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/modals/ModalTrigger.js)

Trình bao bọc phương thức đã được bao phủ bằng một bài kiểm tra; phần thứ hai là để bao gồm các thành phần kích hoạt phương thức. \
Tổng quan về thành phần: nó dựa trên trạng thái  `toggled` biểu thị mức độ hiển thị của  `ModalWrapper`. Nếu `toggled: false`, cửa sổ bật lên bị ẩn, nếu không nó sẽ hiển thị. Hàm  `open()` mở một cửa sổ bật lên trên một phần tử con; nhấp vào sự kiện và hàm  **close ()**  ẩn cửa sổ bật lên trên nút được hiển thị trong ModalWrapper.

**2.1**  Tạo ảnh chụp nhanh:

```
it('render ModalTrigger component correctly', () => {      const ModalTriggerComponent = shallow(<modaltrigger><div></div></modaltrigger>);    expect(ModalTriggerComponent).toMatchSnapshot();});
```

Chúng ta có nên thử nghiệm  `ModalTrigger` với kết xuất prop thành phần? Không; bởi vì  `component` sẽ được hiển thị bên trong  `ModalWrapper` thành phần, nó không phụ thuộc vào thành phần được thử nghiệm. Nó đã được bao phủ với các bài kiểm tra trong các  `ModalWrapper` bài kiểm tra.

**2.2**  Đạo cụ kiểm tra. Chúng tôi có một chỗ dựa  `children` và chúng tôi muốn chắc chắn rằng chúng tôi chỉ có một đứa con.

```
it('ensure to have only one child (control element)', () => {      expect(ModalTriggerComponent.findWhere(node => node.key() === 'modal-control').length).toEqual(1);});
```

**2.3**  Thử nghiệm các loại prop. Đạo cụ trẻ em nên là đồ vật, kiểm tra điều này trong bài kiểm tra tiếp theo:

```
const ModalTriggerComponent = mount(<modaltrigger><div></div></modaltrigger>); it('check children prop type', () => {        expect(ModalTriggerComponent.props().children).toBeObject();});
```

**2.4** Một phần quan trọng của  `ModalTrigger` thành phần là kiểm tra trạng thái.

Chúng tôi có hai trạng thái:

Popup được mở. Để biết rằng phương thức được mở, chúng ta cần kiểm tra trạng thái của nó. Đối với điều này, hãy gọi  `open` hàm từ thể hiện của thành phần và mong rằng  `toggled` trạng thái sẽ là đúng.

```
it('check the modal is opened', () => {      const event = {        preventDefault: () => {},        stopPropagation: () => {}    };    ModalTriggerComponent.instance().open(event);    expect(ModalTriggerComponent.state().toggled).toBeTruthy();});
```

Cửa sổ bật lên được đóng và kiểm tra -  `toggled` trong trạng thái nên là sai.

```
it('check the modal is closed', () => {     ModalTriggerComponent.instance().close();   expect(ModalTriggerComponent.state().toggled).toBeFalsy();});
```

**Danh sách kiểm tra đầy đủ:**  [ModalTrigger.test.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/modals/__tests__/ModalTrigger.test.js)

Bây giờ các phương thức được kiểm tra đầy đủ. Một lời khuyên để kiểm tra các thành phần phụ thuộc lẫn nhau: trước tiên hãy xem qua các thành phần và viết kế hoạch kiểm tra, xác định những gì bạn cần kiểm tra trong từng thành phần, kiểm tra các trường hợp kiểm tra cho từng thành phần và chắc chắn rằng bạn không lặp lại cùng một trường hợp thử nghiệm trong cả hai thành phần. Phân tích cẩn thận các biến thể có thể và tối ưu cho phạm vi kiểm tra.

### 5. Thử nghiệm HOC (Thành phần bậc cao)

Hai phần cuối (kiểm tra trường của HOC và mẫu) được kết nối với nhau. Tôi muốn chia sẻ với bạn cách kiểm tra bố cục trường với HOC của nó.

Giải thích về  `BaseFieldLayout`lý do tại sao chúng ta cần thành phần này và nơi chúng ta sử dụng nó:

* BaseFieldLayout.js là wrapper cho các thành phần hình thức đầu vào như TextInput, CheckboxInput, DateInput, SelectInput vv Tên của họ kết thúc bằng  `-Input` vì chúng tôi sử dụng gói Redux-form và các thành phần này là những thành phần đầu vào Redux dạng logic.
* Chúng tôi cần BaseFieldLayout để tạo bố cục cho các thành phần trường biểu mẫu, đó là kết xuất nhãn, chú giải công cụ, tiền tố (tiền tệ, viết tắt mét vuông, v.v.), biểu tượng, lỗi, v.v.
* Chúng tôi sử dụng nó trong BaseFieldHOC.js để gói inputComponent trong bố cục trường và kết nối nó với dạng redux với sự trợ giúp của   thành phần.

**Danh sách mã cho thành phần được thử nghiệm:**  [BaseFieldHOC.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/hoc/BaseFieldHOC.js)

HOC nhận các thành phần đầu vào của biểu mẫu và trả về các thành phần, được kết nối với biểu mẫu redux.

Phân tích HOC:

* Thành phần này chỉ nhận được một prop ,  `component`. Trước hết, tôi cần tạo thành phần này và bọc nó trong BaseFieldHOC.
* Tiếp theo, tôi cần trang trí bọc HOC với dạng redux để có được trường kết nối với dạng redux.
* Kết xuất trường này bên trong  thành phần React Redux  để làm cho cửa hàng có sẵn cho thành phần được thử nghiệm. Để giả cửa hàng, chỉ cần làm:

```
const store = createStore(() => ({}));  
```

Bây giờ, trước mỗi bài kiểm tra, tôi cần làm tiếp theo:

```
let BaseFieldHOCComponent; beforeEach(() => {      const TextInput = () => { return 'text input'; },        BaseFieldHOCWrapper = BaseFieldHOC(TextInput),        TextField = reduxForm({ form: 'testForm' })(BaseFieldHOCWrapper);    BaseFieldHOCComponent = renderer.create(        <provider store="{store}">            <textfield name="text-input"></textfield>        </provider>    ).toJSON();});
```

Sau đó, thành phần đã sẵn sàng để thử nghiệm:

1\. Tạo ảnh chụp nhanh:

```
it('render correctly component', () => {      expect(BaseFieldHOCComponent).toMatchSnapshot();});
```

2\. Đảm bảo rằng thành phần đầu vào được bọc  `BaseFieldLayout` sau khi kết xuất:

```
it('check input component is wrapped in BaseFieldLayout', () => {      expect(BaseFieldHOCComponent.props.className).toEqual('form-group');});
```

Đó là tất cả, HOC được bảo hiểm. Phần phức tạp nhất trong việc thử nghiệm các thành phần dạng redux được kết nối là chuẩn bị cho trường (trang trí với biểu mẫu redux và cửa hàng thiết lập); phần còn lại là dễ dàng, chỉ cần làm theo hướng dẫn và không có gì khác.

**Danh sách kiểm tra đầy đủ:**  [BaseFieldHOC.test.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/hoc/__tests__/BaseFieldHOC.test.js)

### 6. Kiểm tra biểu mẫu / lĩnh vực

Trường HOC đã được kiểm tra và chúng tôi có thể chuyển sang  `BaseFieldLayout` thành phần.

**Danh sách mã cho thành phần được thử nghiệm:**  [BaseFieldLayout.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/forms/fields/BaseFieldLayout.js)

Hãy mã BaseFieldLayout.js; viết bài kiểm tra theo hướng dẫn trên.

**1. Trước hết, tạo một ảnh chụp nhanh.**

Thành phần này sẽ không được hiển thị nếu không có defaultProps:

* đầu vào
* Các đạo cụ được cung cấp bởi redux-form: các đối tượng đầu vào và meta. Nhập với tên thuộc tính và meta với lỗi thuộc tính và chạm:

```
const defaultProps = {     meta: {        touched: null,        error: null    },    input: {        name: 'field-name'    },    inputComponent: () => { return 'test case'; }}
```

Để sử dụng defaultProps trong mỗi trình bao bọc được kiểm tra, hãy làm như sau:

```
import TestBaseFieldLayout from '../BaseFieldLayout'; const BaseFieldLayout = (props) => <testbasefieldlayout {...defaultprops}="" {...props}=""></testbasefieldlayout>;  
```

Bây giờ chúng tôi đã sẵn sàng để tạo một ảnh chụp nhanh:

```
it('render correctly BaseFieldLayout component', () => {      const BaseFieldLayoutComponent = renderer.create(<basefieldlayout></basefieldlayout>).toJSON();    expect(BaseFieldLayoutComponent).toMatchSnapshot();});
```

**2. Đạo cụ kiểm tra:**

Thành phần này có nhiều đạo cụ. Tôi sẽ đưa ra ví dụ về một vài cái; phần còn lại được kiểm tra bằng cách tương tự.

* Đảm bảo  `icon` prop được hiển thị chính xác:

```
it('render correctly icon prop', () => {      const props = {            icon: <span classname="icon-exclamation"></span>        },        BaseFieldLayoutComponent = mount(<basefieldlayout {...props}=""></basefieldlayout>);        expect(BaseFieldLayoutComponent.find('span').hasClass('icon-exclamation')).toBeTruthy();});
```

* Đảm bảo nội dung chú giải công cụ hiển thị bên cạnh nhãn

```
const props = {          labelTooltipContent: 'tooltip for label'    },    BaseFieldLayoutComponent = mount(<basefieldlayout {...props}=""></basefieldlayout>); it('check prop is rendered', () => {     expect(BaseFieldLayoutComponent.find('span').hasClass('tooltip-icon')).toBeTruthy();});
```

* Kiểm tra  `fieldLink` chống đỡ
  * Đảm bảo  `fieldLink` là null theo mặc định

```
it('check prop is null by default', () => {      const BaseFieldLayoutComponent = shallow(<basefieldlayout></basefieldlayout>);    expect(BaseFieldLayoutComponent.props().fieldLink).toBe(null);});
```

* Đảm bảo trườngLink kết xuất chính xác với giá trị tùy chỉnh

**3. Lỗi kiểm tra:**

```
it('check if field has error', () => {      const props = {            meta: {                touched: true,                error: 'This field is required'            }        },        BaseFieldLayoutComponent = mount(<basefieldlayout {...props}=""></basefieldlayout>);    expect(BaseFieldLayoutComponent.find('.error')).toHaveLength(1);});
```

**Danh sách kiểm tra đầy đủ:**  [BaseFieldLayout.test.js](https://github.com/ned-alyona/testing-jest-enzyme/blob/master/shared/forms/fields/__tests__/BaseFieldLayout.test.js)

### Dòng dưới cùng

Bây giờ bạn có hướng dẫn đầy đủ về cách thực hiện kiểm tra phạm vi bảo hiểm đầy đủ của các thành phần dựa trên cấu trúc dự án. Từ kinh nghiệm của bản thân, tôi đã cố gắng giải thích những gì cần thiết để kiểm tra, theo thứ tự nào và những gì bạn có thể bỏ qua trong phạm vi kiểm tra. Ngoài ra, tôi đã chứng minh các ví dụ về một số thành phần thử nghiệm và phát hiện ra chuỗi bảo hiểm của cơ sở mã hóa. Tôi hy vọng rằng bạn sẽ thấy bài viết này hữu ích và sẽ chia sẻ phản hồi của bạn. Cảm ơn bạn đã đọc.

Bài viết về [thử nghiệm các thành phần phản ứng](https://djangostars.com/blog/what-and-how-to-test-with-enzyme-and-jest-full-instruction-on-react-component-testing/) ban đầu được xuất bản trên blog Django Stars.

* [web dev](https://helpex.vn/it-code/bai-viet-chu-de/web-dev)
* [phản ứng](https://helpex.vn/it-code/bai-viet-chu-de/phan-ung)
* [kiểm tra thành phần](https://helpex.vn/it-code/bai-viet-chu-de/kiem-tra-thanh-phan)
* [hướng dẫn](https://helpex.vn/it-code/bai-viet-chu-de/huong-dan)

4 hữu ích0 bình luận9.5k xemchia sẻ &#x20;

### CÓ THỂ BẠN QUAN TÂM

## [Sử dụng log với Angular 1.5](https://helpex.vn/article/su-dung-log-voi-angular-1.5-60c999663f07bf0a5af5b79c)

![Huỳnh Hòa Hiệp](https://cdn.helpex.vn/upload/2020/08/22/55ee430a8b75f32e34151b41-062658544_50x50.jpg)[Huỳnh Hòa Hiệp](https://helpex.vn/thanh-vien/55ee430a8b75f32e34151b41)·17:00 26/07/201643 phút trước

_(Bài viết giả định ít nhất bạn có kiến ​​thức cơ bản về Angular. Angular là một framework rất kiên định, vì vậy hãy đảm bảo bạn có một số kinh nghiệm với Angular trước khi làm theo các hướng dẫn được trình bày bên dưới.)_

Các bản ghi có thể tích hợp vào bất kỳ khuôn khổ Javascript nào bạn muốn sử dụng. Trước đây, chúng tôi đã kiểm tra việc thêm [Logentries vào một ứng dụng React](https://blog.logentries.com/2016/05/integrating-the-logentries-javascript-library-with-react/) . Bài đăng này sẽ minh họa cách thêm Thông tin đăng nhập vào ứng dụng Angular v1 của bạn bằng cách sử dụng Nhà cung cấp. Kiến trúc Nhà cung cấp của Angular v1 cung cấp một cách mô-đun và mạnh mẽ để thêm chức năng vào các ứng dụng Angular của bạn.

Kiến trúc Angular Provider là một trong những thiết bị tuyệt vời của Angular v1. Tài liệu dành cho Nhà cung cấp có [tại đây](https://docs.angularjs.org/guide/providers) . Trình cung cấp là các đối tượng độc lập được đưa vào và sử dụng bên trong các ứng dụng Angular. Chúng là các khối xây dựng của hầu hết các ứng dụng Angular và thực thi một cấu trúc mô-đun thúc đẩy việc tái sử dụng và làm sạch mã. Có một số loại Nhà cung cấp; chúng ta sẽ sử dụng loại Factory.

Angular Factory là một đối tượng tùy chỉnh được tạo bằng phương pháp Angular factory. Chuyển một nhà máy vào mô-đun Angular của bạn thông qua quá trình tiêm sẽ làm cho đối tượng tùy chỉnh của bạn có thể truy cập bên trong mô-đun. Thông thường, Factory được sử dụng để tạo một đối tượng chỉ được tạo một lần và trả về một đối tượng được chia sẻ. Dịch vụ phía máy khách Logentries rất phù hợp với mẫu Factory vì nó cũng hoạt động như một tài nguyên được chia sẻ. Phần lớn mã được sử dụng trong nhà máy là để khởi tạo đối tượng Logentries. Sau khi khởi tạo, Factory sẽ hiển thị đối tượng Logentries.

Hãy bắt đầu bằng cách xem index.html:

```
<body ng-app="LeAngularSample"><selection></selection><script src="vendor/angular.min.js"></script><script src="vendor/le.min.js"></script><script src="LogEntriesFactory.js"></script><script src="app.js"></script></body>
```

Thẻ ng-app xác định mô-đun chính mà Angular đang tải vào phần thân. LeAngularSample là mô-đun mẫu đang được sử dụng để minh họa thư viện. LogEntriesFactory.js giới thiệu thư viện Logentries. Mã trong app.js chèn và sử dụng mã Logentries, đồng thời chèn thành phần giao diện người dùng của mẫu vào thẻ “\<selection>” tùy chỉnh. Các thẻ còn lại bổ sung các thư viện javascript cần thiết.

Index.html là trang mở đầu. Hãy xem các tệp script đang được nhập, bắt đầu với tệp LogEntriesFactory.js:

```
angular.module('LogEntries',[]).factory('LoggerFactory', function (){  var opts = {};  opts.token = '1234_fake_token';  LE.init(opts);  return LE;})
```

Mã bắt đầu bằng cách tạo một mô-đun Angular mới có tên là “LogEntries”. Mô-đun này là một mô-đun Angular độc lập có khả năng được sử dụng ở mọi nơi. Tệp này có thể được gỡ bỏ từ ứng dụng này và được chèn vào bất kỳ ứng dụng nào.

Mô-đun tạo một đối tượng được gọi là LoggerFactory. LoggerFactory được sử dụng trong mã để tương tác với đối tượng Logentries. LoggerFactory khởi động, đặt mã thông báo Logentries, khởi tạo đối tượng trên mỗi tài liệu Logentries và trả về đối tượng Logentries đã chuẩn bị.

Tệp script khác chứa chỉ thị. Chỉ thị trông như thế này:

```
angular.module('LeAngularSample', ['LogEntries']).directive('selection', function (){  return {    restrict: 'E',    transclude: true,    scope: {},    templateUrl: 'template.html',    controller: function ($scope, LoggerFactory){      $scope.sendError = function (msg){        LoggerFactory.error('Error: ' + msg);      };      $scope.sendWarn = function (msg){        LoggerFactory.warn('Warn: ' + msg);      };      $scope.sendInfo = function (msg){        LoggerFactory.info('Info: ' + msg);      };      $scope.sendLog = function (msg){        LoggerFactory.log('Log: ' + msg);      };    }  }})
```

&#x20;Chỉ thị là cơ chế của Angular để tạo các phần tử giao diện người dùng. Chỉ thị của chúng tôi thay thế thẻ \<selection> bằng nội dung của HTML trong tệp template.html (được hiển thị bên dưới). Các đoạn mã thú vị nhất cho mục đích của chúng tôi là:

* **Injector** - angle.module ('LeAngularSample', \[ _'LogEntries'_ ]) LogEntries là tên của mô-đun có chứa LoggerFactory. Dòng này yêu cầu Angular đưa mô-đun LogEntries vào mô-đun LeAngularSample và làm cho các thành phần của mô-đun LogEntries có thể truy cập được.
* **templateUrl -** templateUrl: 'template.html'Angular thay thế thẻ lựa chọn bằng nội dung của tệp mẫu tại templateUrl
* **controller** - controller: function ($ scope, LoggerFactory) {Bộ điều khiển bao gồm mã được gọi bởi các thành viên của chỉ thị lựa chọn. Lưu ý rằng các chức năng trong bộ điều khiển được gọi trong ng-click của mẫu. Mỗi liên kết từ mẫu sẽ gọi chức năng đối ứng của nó từ bộ điều khiển. LoggerFactory có thể được bao gồm vì mô-đun đã được đưa vào ở trên. Nó nằm trong các tham số chức năng của bộ điều khiển nên nó có thể được sử dụng trong mã của bộ điều khiển.

Tệp html mẫu trông như thế này (với một số văn bản đã bị xóa):

```
<div>….</div><div style= "display: flex; flex-direction: column; width:200px">  <a href="#" ng-click="sendError('Oops')">Submit Error</a>  <a href="#" ng-click="sendWarn('Warning!')">Submit Warning</a>  <a href="#" ng-click="sendInfo('Info')">Submit Info</a>  <a href="#" ng-click="sendLog('Logging')">Submit Log</a></div>
```

Lưu ý bốn dòng với ng-click. Các lệnh ng-click được hiển thị dưới dạng các sự kiện nhấp chuột. Bốn chức năng tương ứng trực tiếp với các phương thức được gọi trong mã bộ điều khiển của chỉ thị. Angular sẽ tự động liên kết các chức năng trong ng-click với chỉ thị.

Mẫu đi kèm với một thiết lập máy chủ nút để hiển thị trang. README có hướng dẫn cách bắt đầu. Sau khi thiết lập và chạy, trang sẽ giống như hình dưới đây. Sử dụng bất kỳ liên kết nào trong số này sẽ gửi nhật ký đến tài khoản Logentries của bạn với loại tương ứng.

Ràng buộc Logentries vào mã Angular của bạn cũng đơn giản như việc đưa vào một mô-đun. Hãy sử dụng LogEntriesFactory.js và sửa đổi nó để hoạt động trong ứng dụng Angular của bạn. Việc đưa thư viện phía máy khách Logentries vào mã của bạn thật dễ dàng nhờ vào các thành phần mô-đun và chèn của Angular.

Bạn có thể tìm thấy repo GitHub với đầy đủ mẫu tại  [https://github.com/LogentriesCommunity/Logentries-Angular-Sample](https://github.com/LogentriesCommunity/Logentries-Angular-Sample)

* [angular](https://helpex.vn/it-code/cau-hoi-chu-de/angular)
* [logentries](https://helpex.vn/it-code/cau-hoi-chu-de/logentries)
* [code](https://helpex.vn/it-code/cau-hoi-chu-de/code)
* [integration](https://helpex.vn/it-code/cau-hoi-chu-de/integration)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)

9 hữu ích0 bình luận4.8k xemchia sẻ

## [Làm cho các khái niệm và thông số kỹ thuật web được trình bày dưới dạng Trợ giúp thời gian thực trong thiết kế API ...](https://helpex.vn/article/lam-cho-cac-khai-niem-va-thong-so-ky-thuat-web-duoc-trinh-bay-duoi-dang-tro-giup-thoi-gian-thuc-trong-thiet-ke-api-...-60c9903a3f07bf0a5af5b71e)

![Đặng Hải Ðăng](https://cdn.helpex.vn/upload/2016/05/23/55ee433b8b75f32e341558c7-114950728_50x50.jpg)[Đặng Hải Ðăng](https://helpex.vn/thanh-vien/55ee433b8b75f32e341558c7)·17:00 05/09/2016hôm qua

Tôi đã lấy [kho lưu trữ GitHub](https://github.com/dret/webconcepts) cho hoạt động của [Khái niệm web](http://webconcepts.info/) ( [@dret](https://twitter.com/dret) ) của Erik Wilde và phân nhánh nó, sau đó tạo một số JSON mà tôi có thể sử dụng để nhập vào hệ thống giám sát API của mình. Tôi đã thêm thông số kỹ thuật vào hệ thống lập lịch Tweet và LinkedIn của mình theo cách thủ công, nhưng tôi vẫn quên quay lại trang web và thêm các mục khác. Vì vậy, tôi muốn tiếp tục và nhập tất cả các khái niệm và thông số kỹ thuật, đồng thời lên lịch cho các bài đăng trên Twitter và LinkedIn cho mọi thứ, trong vài tháng tới.

[Đầu tiên, tôi tạo JSON cho các khái niệm, sau đó tôi tạo JSON cho các thông số kỹ thuật, bạn có thể xem ở đây.](http://apievangelist.com/2016/09/01/making-web-concepts-and-specs-present-as-real-time-help-in-api-design-tooling/)

Tôi đã bỏ qua các mối quan hệ giữa các khái niệm và thông số kỹ thuật, vì tôi sẽ chỉ liên kết đến Các khái niệm web và để mọi người tự khám phá. Khi tôi xem qua JSON, tôi nghĩ về lý do tại sao những khái niệm và thông số kỹ thuật này không có sẵn trong công cụ thiết kế API dưới dạng trợ giúp và chú giải công cụ, để các nhà thiết kế và kiến ​​trúc sư API có thể học hỏi từ chúng và được nhắc nhở trong thời gian thực — tạo ra các API của họ.&#x20;

Có vẻ như cần có tính năng tự động hoàn thành cho các trường tiêu đề HTTP, mã trạng thái HTTP và các mục liên quan khác nếu cần. Có rất nhiều kiến ​​thức về web trong công việc của Erik, và thông qua các khái niệm và thông số kỹ thuật web mà anh ấy đã sắp xếp, có vẻ như chúng sẽ có sẵn theo mặc định trong các dịch vụ và công cụ thiết kế API, và bắt đầu được đưa vào các IDE như Atom, Eclipse, và Visual Studio. Có lẽ họ đã có, và tôi chỉ không biết.

* [api](https://helpex.vn/it-code/cau-hoi-chu-de/api)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)

0 hữu ích0 bình luận3.1k xemchia sẻ

## [Đánh giá về tính bất biến trong Ruby](https://helpex.vn/article/danh-gia-ve-tinh-bat-bien-trong-ruby-60c98e623f07bf0a5af5b701)

![Huỳnh Mỹ Tâm](https://cdn.helpex.vn/upload/2016/01/02/55ee43088b75f32e341518e8-083934015_50x50.jpg)[Huỳnh Mỹ Tâm](https://helpex.vn/thanh-vien/55ee43088b75f32e341518e8)·17:00 13/09/20168 giờ trước

Trạng thái có thể thay đổi được chia sẻ là nguồn gốc của rất nhiều lỗi. Khi hai hoặc nhiều đối tượng sử dụng cùng một phần dữ liệu có thể thay đổi, tất cả chúng đều có khả năng phá vỡ lẫn nhau theo những cách khó có thể gỡ lỗi. Tuy nhiên, nếu dữ liệu được chia sẻ là bất biến, các đối tượng này không thể ảnh hưởng lẫn nhau và được tách rời một cách hiệu quả.

Bài viết này là một đánh giá về các tùy chọn có sẵn cho Rubyists liên quan đến tính bất biến. Chúng ta sẽ xem xét các tính năng tích hợp của Ruby 2.3 và một vài viên ngọc.

### Cấp đông Lib tiêu chuẩn

Hãy bắt đầu với `freeze`phương thức từ thư viện chuẩn:

#### Đối tượng # đóng băng

Ngăn chặn các sửa đổi thêm đối với `obj`. A `RuntimeError`sẽ được nâng lên nếu cố gắng sửa đổi. Không có cách nào để giải phóng một đối tượng bị đóng băng. Xem thêm `Object#frozen?`.

Phương thức này trả về tự.

```
a = [ "a", "b", "c" ]a.freezea << "z"
```

sản xuất:

```
prog.rb:3:in `<<': can't modify frozen Array (RuntimeError)from prog.rb:3
```

Đối tượng của các lớp sau đây luôn đông lạnh: `Fixnum`, `Bignum`, `Float`, `Symbol`.

Các `freeze`phương pháp sẽ làm việc cho hầu hết các đối tượng, trong đó có trường hợp của các tầng lớp người dùng định nghĩa:

```
class Foo  def mutate_self    @x = 5  endend f = Foo.newf.freezef.mutate_self #=> RuntimeError: can't modify frozen Foo
```

Ngoại lệ duy nhất là các lớp kế thừa từ `BasicObject`. Các `freeze`phương pháp được định nghĩa trên `Object`, vì vậy nó không phải là có sẵn với các trường hợp `BasicObject`:

```
class BasicFoo < BasicObject; endbf = BasicFoo.newbf.freeze #=> NoMethodError: undefined method `freeze' for #<BasicFoo:0x007f912b9c3060>
```

Bạn sẽ thường thấy `freeze`được sử dụng khi gán hằng số, để đảm bảo rằng các giá trị không thể bị thay đổi. Điều này là do việc gán lại một biến hằng số sẽ tạo ra một cảnh báo, nhưng việc thay đổi một giá trị hằng số thì _không_ .

```
module Family  NAMES = ['Tom', 'Dane']end # mutation is allowedFamily::NAMES << 'Alexander'p Family::NAMES #=> ["Tom", "Dane", "Alexander"] # reassignment triggers a warningFamily::NAMES = ['some', 'other', 'people']#=> warning: already initialized constant Family::NAMES
```

Vì vậy, nếu bạn muốn đảm bảo rằng các hằng số của bạn thực sự không đổi, bạn cần cố định giá trị:

```
module Family  NAMES = ['Tom', 'Dane'].freezeend
```

Vấn đề chính của `freeze`phương pháp là nó _nông_ , trái ngược với đệ quy. Ví dụ: một mảng cố định không thể có thêm, xóa hoặc thay thế các phần tử, nhưng bản thân các phần tử hiện tại vẫn có thể thay đổi:

```
module Family  NAMES = ['Tom', 'Dane'].freezeend Family::NAMES.first.upcase!p Family::NAMES #=> ["TOM", "Dane"]
```

### Frozen String Literals Trong Ruby 2.3

Bạn có thể nhận thấy rằng các biểu tượng và số được tự động đóng băng trong Ruby. Ví dụ, không thể thực hiện `add!`phương pháp này :

```
x = 5x.add!(2)x == 7 #=> this can't be true
```

Trong hầu hết các ngôn ngữ, ký tự chuỗi cũng không thay đổi, giống như số và ký hiệu. Tuy nhiên, trong Ruby, tất cả các chuỗi đều có thể thay đổi theo mặc định.

Điều này sẽ thay đổi trong phiên bản chính tiếp theo của Ruby. Tất cả các ký tự chuỗi sẽ không thay đổi theo mặc định trong Ruby 3, nhưng điều đó vẫn còn vài năm nữa. Trong thời gian chờ đợi, chức năng này có thể được kích hoạt tùy chọn kể từ Ruby 2.3.

Có một tùy chọn dòng lệnh có sẵn cho phép các ký tự chuỗi cố định trên toàn cầu:

```
ruby --enable-frozen-string-literal whatever.rb
```

Thật không may, điều này sẽ phá vỡ rất nhiều mã và đá quý đã tồn tại trước đó, bởi vì hầu hết mã được viết với giả định rằng các ký tự chuỗi có thể thay đổi được.

Cho đến khi mã cũ hơn được cập nhật để xử lý các chuỗi bị cố định, tốt hơn nên bật tùy chọn này trên cơ sở từng tệp bằng cách sử dụng “nhận xét kỳ diệu” này ở đầu mỗi tệp:

```
# frozen_string_literal: true greeting = 'Hello'greeting.upcase! #=> RuntimeError: can't modify frozen String
```

Khi nhận xét kỳ diệu này tồn tại, chuỗi ký tự bên trong tệp sẽ bị đóng băng theo mặc định, nhưng mã trong các tệp khác sẽ không bị ảnh hưởng.

Khi bạn thực sự muốn một chuỗi có thể thay đổi, bạn phải tạo một `String#new`chuỗi bằng hoặc sao chép một chuỗi cố định bằng cách sử dụng `String#dup`:

```
# frozen_string_literal: true # this string is mutablex = String.new('Hello')x.upcase!puts x #=> 'HELLO' # and so is thisy = 'World'.dupy.upcase!puts y #=> 'WORLD'
```

### Các `ice_nine`Gem - Freezing Recursive

Nó chỉ ra rằng việc đóng băng một cách đệ quy một đối tượng đúng cách là một chút khó khăn, nhưng may mắn thay có một viên ngọc quý cho điều đó. Các [`ice_nine`đá quý](https://github.com/dkubb/ice_nine) áp dụng `freeze`phương pháp đệ quy, đảm bảo rằng một đối tượng là thực sự đông lạnh:

```
require 'ice_nine' module Family  NAMES = IceNine.deep_freeze(['Tom', 'Dane'])end Family::NAMES.first.upcase!#=> RuntimeError: can't modify frozen String
```

Đá quý cũng cung cấp một phần mở rộng cốt lõi tùy chọn để xác định `Object#deep_freeze`, để thuận tiện:

```
require 'ice_nine'require 'ice_nine/core_ext/object' module Family  NAMES = ['Tom', 'Dane'].deep_freezeend
```

### Các `values`Gem - Các lớp Immutable Struct-Like

Thay vì đóng băng các đối tượng có thể thay đổi, thường tốt hơn là tạo các đối tượng không thể thay đổi theo mặc định. Đây là nơi mà [`values`đá quý](https://github.com/tcrayford/values) rất hữu ích.

Nếu bạn đã quen thuộc với [`Struct`thư viện tiêu chuẩn](http://ruby-doc.org/core-2.3.0/Struct.html) , `values`thì về cơ bản gem cũng giống như vậy, ngoại trừ việc nó là bất biến theo mặc định.

Đây là một số mã ví dụ:

```
require 'values' # `Value.new` creates a new class, just like `Struct`Person = Value.new(:name, :age) # The `new` class method works just like `Struct`tom = Person.new('Tom', 28)puts tom.age #=> 28 # There is also the `with` class method, that creates an# object given a hashdane = Person.with(name: 'Dane', age: 42)puts dane.age #=> 42 # You can use the `with` instance method to create new objects# based existing objects, with some attributes changedben = tom.with(name: 'Ben')p ben #=> #<Person name="Ben", age=28>p tom #=> #<Person name="Tom", age=28> # Unlike `Struct`, objects do not have any mutating methods definedtom.name = 'Ben'#=> NoMethodError: undefined method `name=' for #<Person name="Tom", age=28>
```

Cũng giống như `Struct`các lớp, các `Value`lớp này có thể có các phương thức tùy chỉnh:

```
Fungus = Value.new(:genus, :species, :common_name) do  def display_name    "#{common_name} (#{genus} #{species})"  endend f = Fungus.new('Amanita', 'muscaria', 'Fly agaric')puts f.display_name #=> Fly agaric (Amanita muscaria)
```

Không giống như `Struct`các lớp, các lớp này sẽ tạo ra lỗi nếu thiếu bất kỳ thuộc tính nào khi tạo. Đây là một điều tốt, vì nó cảnh báo bạn về những lỗi tiềm ẩn thay vì im lặng bỏ qua chúng.

```
Person = Value.new(:name, :age) Person.new('Tom') #=> ArgumentError: wrong number of arguments, 1 for 2Person.with(age: 28) #=> ArgumentError: Missing hash keys: [:name] (got keys [:age])
```

Các lớp này chỉ là bất biến nông, giống như `freeze`phương thức tích hợp sẵn. Bản thân các đối tượng không thể thay đổi, nhưng các thuộc tính của chúng vẫn có thể thay đổi được.

```
tom = Person.new('Tom', 28)tom.name.upcase!p tom #=> #<Person name="TOM", age=28>
```

Toàn bộ viên ngọc chỉ có khoảng [100 dòng mã](https://github.com/tcrayford/Values/blob/b3b57abd344806f32b15126a40551414ebd4a01b/lib/values.rb) , vì vậy rất dễ hiểu về toàn bộ.

Đối với phần lớn các tình huống mà bạn sẽ sử dụng `Struct`, tôi nghĩ `Value`các lớp học là lựa chọn tốt hơn. Đối với những tình huống hiếm hoi mà bạn đang cố gắng đạt được từng giọt hiệu suất cuối cùng, `Struct`vẫn là lựa chọn tốt hơn, ít nhất là trên MRI. Điều đó không có nghĩa là `Value`các lớp chậm - chúng có hiệu suất tương tự như bất kỳ lớp Ruby nào khác, nếu không muốn nói là tốt hơn do quá trình băm tích cực. Trong MRI, `Struct`lớp được thực hiện theo một cách hiệu quả bất thường. Trong các triển khai khác, chẳng hạn như JRuby, có thể không có sự khác biệt về hiệu suất.

Nếu bạn không sử dụng `Struct`các lớp, bạn có thể tự hỏi tại sao và nơi bạn muốn sử dụng một trong hai lớp đó. Nguồn tốt nhất mà tôi có thể chỉ cho bạn là [Giá trị của các Giá trị của Rich Hickey](https://www.infoq.com/presentations/Value-Values) . Cuối cùng, nó tóm tắt tất cả những lợi ích của _ngữ nghĩa giá trị_ , mà Rich giải thích chi tiết.

### Các `adamantium`Gem - Tự động Recursive Freezing

Các [`adamantium`đá quý](https://github.com/dkubb/adamantium) cung cấp đóng băng đệ quy tự động cho các lớp học của Ruby qua `ice_nine`đá quý.

```
require 'adamantium' class Person  include Adamantium   attr_reader :name, :age   def initialize(name, age)    @name = name    @age = age  end   def with_name(new_name)    transform do      @name = new_name    end  endend tom = Person.new('Tom', 28)dane = tom.with_name('Dane') p tom  #=> #<Person:0x007f90b182bb28 @name="Tom", @age=28 ...p dane #=> #<Person:0x007f90b0b28048 @name="Dane", @age=28 ...
```

Adamantium hoạt động bằng cách ghi đè `new`phương thức lớp. Sau khi một đối tượng đã được cấp phát và `initialize`phương thức của nó đã được chạy, nó sẽ bị đóng băng bằng cách sử dụng `ice_nine`gem. Điều này có nghĩa là bạn có thể thay đổi đối tượng từ bên trong `initialize`, nhưng không bao giờ lặp lại.

Để tạo các đối tượng bất biến mới từ những đối tượng hiện có, có một `transform`phương pháp. Điều này hoạt động bằng cách tạo ra một bản sao có thể biến đổi, chạy một khối đột biến trên bản sao, sau đó đóng băng sâu bản sao đó trước khi trả lại. Bạn có thể xem một ví dụ về điều này trong `with_name`phương pháp trên.

Adamantium yêu cầu nhiều tấm boilerplate hơn `values`gem, nhưng nó thực hiện đóng băng đệ quy thích hợp. Nó cũng có chức năng tự động ghi nhớ và đóng băng các giá trị trả về của các phương thức.

### Các `anima`Gem - includable Value Semantics

Các [`anima`viên ngọc](https://github.com/mbj/anima) cơ bản là một hỗn hợp của `values`đá quý và `adamantium`đá quý.

```
require 'anima' class Person  include Anima.new(:name, :age)end tom = Person.new(name: 'Tom', age: 28)rhi = tom.with(name: 'Rhiannon') p tom #=> #<Person name="Tom" age=28>p rhi #=> #<Person name="Rhiannon" age=28>
```

Nó có tính ngắn gọn của `values`đá quý và sử dụng Adamantium để đóng băng đệ quy tự động.

Hãy coi đây là phiên bản có trọng lượng nặng của `values`viên đá quý. Nó có một vài tính năng hơn, nhưng nó cũng mang lại lăm đá quý như phụ thuộc: `ice_nine`, `memoizable`, `abstract_type`, `adamantium`và `equalizer`. Để so sánh, `values`gem không có phụ thuộc và được triển khai trong một tệp duy nhất với khoảng 100 dòng mã.

### Các `hamster`Gem - Persistent Cấu trúc dữ liệu

Các [`hamster`đá quý](https://github.com/hamstergem/hamster) cung cấp một tập các lớp cấu trúc dữ liệu liên tục. Các lớp học này thay thế bất biến cho các lớp học của Ruby tiêu chuẩn như `Hash`, `Array`, và `Set`. Chúng hoạt động theo kiểu tương tự như các viên ngọc khác - không thể sửa đổi các đối tượng, nhưng bạn có thể tạo các đối tượng mới dựa trên các đối tượng hiện có.

Làm việc với các giá trị không thay đổi thường đòi hỏi nhiều bản sao, giống như sao chép toàn bộ mảng chỉ để thêm một phần tử mới. Cấu trúc dữ liệu liên tục cung cấp hiệu suất tốt hơn cho các loại hoạt động này bằng cách giảm số lượng đối tượng cần được sao chép và sử dụng lại càng nhiều đối tượng càng tốt.

Ví dụ, nếu bạn muốn tạo một mảng được cố định từ một mảng đã được cố định hiện có, bạn sẽ phải làm như thế này trong Ruby đơn giản:

```
original = [1, 2, 3].freeze new_one = original.dup # makes a copynew_one << 4new_one.freeze p original #=> [1, 2, 3]p new_one  #=> [1, 2, 3, 4]
```

Với `Hamster::Vector`, điều này sẽ trông giống như:

```
require 'hamster' original = Hamster::Vector[1, 2, 3]new_one = original.add(4) p original #=> Hamster::Vector[1, 2, 3]p new_one  #=> Hamster::Vector[1, 2, 3, 4]
```

Trong `Hamster::Vector`phiên bản, `new_one`có thể không phải là một bản sao hoàn toàn của `original`. Bên trong, `new_one`giá trị có thể chỉ giữ `4`một tham chiếu cộng với `original`. Chia sẻ trạng thái bên trong theo cách này cải thiện cả tốc độ và mức sử dụng bộ nhớ, đặc biệt là đối với các đối tượng lớn. Tất cả điều này diễn ra tự động dưới mui xe, vì vậy bạn không cần phải suy nghĩ về nó.

Để có cái nhìn tổng quan về chủ đề này, tôi giới thiệu một bài nói chuyện Rich Hickey khác: [Cấu trúc dữ liệu liên tục và tài liệu tham khảo được quản lý](https://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey) . Chuyển tiếp đến 23:49 để đến phần cụ thể về cấu trúc dữ liệu liên tục.

### Đối tượng giá trị Virtus

Tôi muốn nhanh chóng đề cập đến [`virtus`đá quý](http://www.rubypigeon.com/posts/a-review-of-immutability-in-ruby/) , mặc dù tôi khuyên không nên sử dụng nó. Nó có một số chức năng "đối tượng giá trị" hoạt động rất giống với `values`and `anima`gems, nhưng với các tính năng bổ sung xung quanh xác thực kiểu và cưỡng chế.

```
require 'virtus' class Person  include Virtus.value_object   values do    attribute :name, String    attribute :age,  Integer  endend tom = Person.new(name: 'Tom', age: 28)sue = tom.with(name: 'Sue') p tom #=> #<Person name="Tom" age=28>p sue #=> #<Person name="Sue" age=28>
```

Về lý do tại sao tôi khuyên bạn không nên sử dụng nó, hãy để tôi trích dẫn tác giả của viên đá quý Piotr Solnica trong [chủ đề reddit này](https://www.reddit.com/r/ruby/comments/3sjb24/virtus_to_be_abandoned_by_its_creator/) :

Lý do tại sao tôi không còn quan tâm đến việc làm việc trên nền tảng công nghệ không phải là điều tôi có thể giải thích dễ dàng, nhưng tôi sẽ cố gắng.

\[…]

\[Nó] đã được tối ưu hóa cho một trường hợp sử dụng cụ thể là lưu trữ dữ liệu từ một biểu mẫu web để làm cho cuộc sống của chúng ta đơn giản hơn và chức năng này chỉ đơn giản là được đưa vào ORM

\[…]

Tôi đã phạm phải một sai lầm mà trước đây đã từng mắc phải từ ActiveRecord.

\[…]

Tôi đã mất một lúc để hiểu điều gì đang thực sự diễn ra. Virtus là viên ngọc mang lại di sản của DataMapper, mang lại di sản của… ActiveRecord. Đó là một quá trình dài để hiểu một số vấn đề cơ bản, khi tôi đã hiểu chúng, tôi bắt đầu làm việc trên các thư viện mới để giải quyết những vấn đề đó theo cách tốt hơn. Tôi càng làm việc nhiều hơn với những thư viện đó, tôi càng thấy rõ ràng rằng Virtus sẽ phải được thay đổi hoàn toàn và sẽ không còn phục vụ cùng mục đích nếu tôi muốn xây dựng nó theo cách mà tôi nghĩ là đúng.

Virtus đã cố gắng trở thành một con dao quân đội Thụy Sĩ phổ biến cho các cuộc cưỡng chế, như một hệ quả tự nhiên của việc được trích xuất từ ​​một ORM có nhiều điểm chung với ActiveRecord, nó đã cố gắng làm quá nhiều, với rất nhiều hành vi ngầm, các trường hợp cạnh kỳ lạ, hiệu suất các vấn đề và DSL phức tạp.

\[…]

Hơn nữa, thuộc tính DSL với nhiều tùy chọn là một mô hình chống. Đó là những gì tôi đã học được theo thời gian. Và nó không kết thúc ở đây - thiếu an toàn kiểu thực tế là một vấn đề, Virtus có một chế độ nghiêm ngặt nhưng không thể làm cho nó đúng trong một thư viện được sử dụng trong nhiều bối cảnh khác nhau.

### Sắp tới tiếp theo: Phong cách kỷ luật và chức năng

Bài viết này chỉ đề cập đến các tùy chọn thuộc loại nặng tay. Tất cả đều yêu cầu đá quý hoặc mã bổ sung.

Bài tiếp theo sẽ nói về cái mà tôi gọi là “phong cách chức năng” - sử dụng kỷ luật để tránh đột biến, thay vì thực thi tính bất biến. Nó không yêu cầu thêm đá quý và không cần thêm mã. Giữ nguyên!

* [ruby](https://helpex.vn/it-code/cau-hoi-chu-de/ruby)
* [code](https://helpex.vn/it-code/cau-hoi-chu-de/code)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)
* [immutable](https://helpex.vn/it-code/cau-hoi-chu-de/immutable)
* [example](https://helpex.vn/it-code/cau-hoi-chu-de/example)

5 hữu ích0 bình luận2.7k xemchia sẻ

## [Chuẩn bị Hộp công cụ của bạn cho Khuôn khổ Sharepoint](https://helpex.vn/article/chuan-bi-hop-cong-cu-cua-ban-cho-khuon-kho-sharepoint-60c98ccc3f07bf0a5af5b6e3)

![Đặng Hướng Thiện](https://cdn.helpex.vn/upload/2019/08/19/55ee433b8b75f32e34155929-040323564_50x50.jpg)[Đặng Hướng Thiện](https://helpex.vn/thanh-vien/55ee433b8b75f32e34155929)·17:00 22/09/201617:06:10 15/08/2021

Với SharePoint Framework, các nhà phát triển có thể sử dụng các khung như Angular, WebPack & Kendo UI để thiết kế SharePoint UI tùy chỉnh. Tìm hiểu con đường bạn nên đi.\
\
Vào tháng bảy, chúng tôi đã ở một ngã ba. Chúng tôi vừa kết thúc một mô-đun SharePoint On-Premises mới với Angular v1.4 và ngOffice UI Fabric. Mô-đun trước đó của chúng tôi là Angular v1.2 với UI Bootstrap. Chúng tôi biết rằng SharePoint Framework sắp ra mắt và thậm chí cuối cùng sẽ được đưa xuống On-Premise vào năm 2017. Ngoài ra, với tốc độ JavaScript di chuyển, về cơ bản chúng tôi cần phải làm mới hộp công cụ của mình ba tháng một lần.

Đã đến lúc phải thực hiện một chút nghiên cứu — và vì vậy chúng tôi đã làm.

### Chọn một con đường <a href="#toc_1" id="toc_1"></a>

Nghiên cứu này đã đạt đến đỉnh cao trong việc viết [Sách chuẩn bị cho Hộp công cụ của bạn cho Khuôn khổ SharePoint với ](http://www.telerik.com/campaigns/kendo-ui/sharepoint-framework-ui-customization-angular)[Sách](https://github.com/johnnliu/contract-register) trắng về [giao diện người dùng Angular, Webpack và Kendo](http://www.telerik.com/campaigns/kendo-ui/sharepoint-framework-ui-customization-angular) , được xây dựng xung quanh [bản trình diễn Đăng ký Hợp đồng (GitHub) này](https://github.com/johnnliu/contract-register) . Các công nghệ chúng tôi chọn chủ yếu tập trung vào những gì chúng tôi đã quen thuộc, nhưng cũng những gì chúng tôi biết sẽ được hỗ trợ trong SharePoint Framework. Chúng tôi đã kiểm tra kỹ với các ví dụ từ nhiều chuyên gia đã viết blog về trải nghiệm [SharePoint Dev Kitchen](https://blog.mastykarz.nl/sharepoint-dev-kitchen/) của họ và đối chiếu với tất cả các [video PnP SPFx](https://www.youtube.com/channel/UC_mKdhw-V6CeCM7gTo_Iy7w) .

#### Angular <a href="#toc_2" id="toc_2"></a>

Chúng tôi bắt đầu với Angular và [Kendo UI](http://www.telerik.com/kendo-ui/ui-for-office-365-sharepoint) bằng Gulp, nhưng quá trình xây dựng liên quan đến việc đóng gói vào CDN, vì vậy chúng tôi đã thêm WebPack.

Cuối cùng, thay vì nói chuyện trực tiếp với SharePoint hoặc sử dụng các thành phần Kendo UI DataSource (cũng hỗ trợ SharePoint REST), tôi muốn chúng tôi sử dụng [pnp-js-core](https://github.com/OfficeDev/PnP-JS-Core) — một API thông thạo để nói chuyện với điểm cuối REST từ nhóm Office PnP.

Chúng tôi đã chuyển phiên bản của mình lên [Angular v1.5](https://toddmotto.com/exploring-the-angular-1-5-component-method/) vì chúng tôi muốn ở vị trí tốt nhất để có thể "triển khai sản xuất" ngay hôm nay, nhưng vẫn ghi vào thông số kỹ thuật của thành phần sẽ là tiêu chuẩn trong Angular v2. Hơn nữa, SPFx-webparts sẽ chạy trong Ứng dụng di động SharePoint trong tương lai. Vì vậy, không cần phải làm thêm quá nhiều công việc một cách rõ ràng — chúng tôi đang xây dựng hướng tới tương lai nơi chúng tôi xây dựng và nó chạy trong trình duyệt và trên thiết bị di động.

#### PnP JS Core <a href="#toc_3" id="toc_3"></a>

Đối với PnP JS Core, API thông thạo là một niềm vui khi làm việc cùng và cách thực hiện phân phối khá kỳ diệu. Trong v1.0.3, thư viện cũng bắt đầu hỗ trợ NodeJS, có nghĩa là bạn có thể sử dụng cùng một mã JavaScript để tương tác với SharePoint từ cả trình duyệt và máy chủ. Viết cùng một đoạn mã chạy ở cả hai nơi là chén thánh mà chúng ta luôn hướng tới. Nếu JavaScript có thể chạy ở mọi nơi, thì các thư viện chúng ta muốn sử dụng sẽ có thể chạy ở mọi nơi.

#### WebPack <a href="#toc_4" id="toc_4"></a>

Khi chúng tôi tìm hiểu sâu hơn về điều kỳ diệu đó là [WebPack](https://github.com/webpack/webpack) , điều kỳ diệu của nó đã được hé mở. Trong [video](https://www.youtube.com/watch?v=rP8d_0MdedY) giới thiệu nhỏ này mà [chúng tôi đã quay cho Đăng ký hợp đồng](https://www.youtube.com/watch?v=rP8d_0MdedY) , bạn có thể nhận thấy WebPack đưa mã vào trình duyệt nhanh như thế nào — ứng dụng đã sẵn sàng trước khi thanh bộ Office 365 xuất hiện. Đây là một tác dụng phụ của việc tách mã. WebPack không chỉ đơn thuần liên hệ và hủy xác minh tệp JS, nó còn phóng các mô-đun JavaScript vào trình duyệt như một khẩu súng ngắn — tất cả cùng một lúc. WebPack gần như loại bỏ sự cần thiết của Gulp trong quá trình này. Gulp vẫn cần thiết để bắt đầu các tác vụ khác nhau, nhưng WebPack thực hiện công việc bên dưới và nó thực sự hoạt động tốt.

#### TypeScript <a href="#toc_5" id="toc_5"></a>

Chúng tôi đã tranh luận rất nhiều về TypeScript. Nó sẽ làm cho IntelliSense và kiểm tra mã tốt hơn, nhưng chúng tôi quyết định loại bỏ nó. Đáng lẽ ra phải học thêm "một điều nữa", và danh sách đã dài ra. Hãy xem mã chúng tôi đã viết cho [sổ đăng ký hợp đồng](https://github.com/johnnliu/contract-register) (GitHub) và cho chúng tôi biết nếu chúng tôi đã lựa chọn đúng.

#### Giao diện người dùng Kendo <a href="#toc_6" id="toc_6"></a>

Tôi cũng muốn nói về [Kendo UI](http://www.telerik.com/blogs/announcing-angular-kendo-ui) . Tôi đã không sử dụng Kendo UI trước dự án này. Đồng nghiệp của tôi và đồng tác giả của whitepaper này, Bart Bouwhuis, là một người hâm mộ lớn và anh ấy liên tục cho tôi thấy những đặc điểm thú vị. Sách trắng không phải là quảng cáo cho Tiến bộ (trước đây là Telerik), nhưng tôi cũng nghĩ rằng chúng ta không thực sự nói về giao diện người dùng Kendo nhiều như chúng ta nên làm.

Cảm giác của tôi là các thành phần Kendo UI đã thực sự trưởng thành — trong một số lĩnh vực cạnh tranh hoặc vượt quá UI Bootstrap.  Dễ dàng thêm các thành phần như  [Grid](http://demos.telerik.com/kendo-ui/grid/index) ,  [Window](http://demos.telerik.com/kendo-ui/window/index) và  [PDF export](http://demos.telerik.com/kendo-ui/grid/pdf-export) . Chúng đã hỗ trợ  [chủ đề Office 365](http://docs.telerik.com/kendo-ui/styles-and-layout/appearance-styling) , vì vậy chúng không có vẻ gì là lạc hậu.

Telerik hỗ trợ Angular v1 và họ vừa phát hành [bản xem trước dành cho nhà phát triển cho Angular 2](http://www.telerik.com/kendo-angular-ui/) . Cuối cùng, chúng tôi đã dành rất ít thời gian để làm cho các thành phần hoạt động với chúng tôi — trong hầu hết các trường hợp, chúng tôi đã tìm kiếm ví dụ trên [Telerik Docs](http://docs.telerik.com/kendo-ui/introduction) và nó chỉ hoạt động! Một điều may mắn nữa là chúng tôi không bao giờ cần phải chỉnh sửa CSS để khắc phục một số vấn đề về kiểu dáng. Đó thực sự là một vấn đề khá nhức nhối với UI Bootstrap hoặc ngOfficeUIFnai.

### Bản xem trước dành cho nhà phát triển SharePoint Framework (SPFx) <a href="#toc_7" id="toc_7"></a>

Mọi thứ đang chuyển động nhanh chóng. Bản xem trước dành cho nhà phát triển SPFx được đưa ra trong quá trình viết whitepaper. Nhớ chuyến tàu đó chứ? Hóa ra nó đến _rất_ nhanh. Chúng tôi thích tốc độ chạy của nhóm SharePoint. Nhưng nó có nghĩa là chúng tôi có một số lưu ý để thêm vào.

Thứ nhất, whitepaper không chỉ về SPFx. Đó là về SharePoint như một nền tảng với AngularJS và WebPack, đồng thời có những phần đó sẵn sàng và được căn chỉnh cho SPFx. Đó là về một bộ công cụ cốt lõi ổn định hoạt động tốt cùng nhau. Đó là về một bộ công cụ chạy ngày nay trên SharePoint 2016, SharePoint 2013 và SharePoint Online. Bạn đã có thể sử dụng chúng mà không cần phải đợi SPFx.

Thứ hai, với SPFx thậm chí còn có nhiều cơ hội hơn. Chúng tôi sẽ hỗ trợ tốt hơn cho các chế độ Angular và SPA — hiện tại cả hai kịch bản đều chưa được phát hành. Vì vậy, câu chuyện sẽ phát triển và trở nên tuyệt vời hơn. Bản demo đăng ký hợp đồng có thể chạy ở chế độ phần mềm web SPFx — trong trường hợp đó, mã để khởi chạy ứng dụng và thiết lập các danh sách khác nhau sẽ đi vào bảng thuộc tính phần web. Chỉ người đóng góp mới có quyền truy cập vào việc tạo danh sách.&#x20;

Cách chúng tôi quyết định quản lý dòng chảy liên tục này là xuất bản bản cập nhật blog khi SPFx chính thức được phát hành để lấp đầy “khoảng trống” —các điều chưa biết về thời điểm xuất bản whitepaper. Vì vậy, hãy theo dõi bản cập nhật như vậy, có thể là sau Microsoft Ignite. Chúng tôi cũng có một loạt các [ý tưởng điên rồ](https://github.com/johnnliu/contract-register/issues) được liệt kê cho Sổ đăng ký Hợp đồng. Vì vậy, nếu bạn muốn có được bàn tay của bạn bẩn và đi, chúng tôi đang thực hiện các yêu cầu kéo!

### Tham gia ngay hôm nay <a href="#toc_8" id="toc_8"></a>

Thời gian để tham gia luôn là bây giờ (hoặc ngay khi bạn có thể). Ở đây, chúng tôi trình bày hai tháng làm việc thể hiện nỗ lực hết mình của chúng tôi nhằm thúc đẩy các công nghệ web hiện đại và SharePoint như một nền tảng.&#x20;

* [sharepoint](https://helpex.vn/it-code/cau-hoi-chu-de/sharepoint)
* [framework](https://helpex.vn/it-code/cau-hoi-chu-de/framework)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)

3 hữu ích0 bình luận6.2k xemchia sẻ

## [Cách sử dụng bảng điều khiển JavaScript để kiểm tra kỹ thuật web](https://helpex.vn/article/cach-su-dung-bang-dieu-khien-javascript-de-kiem-tra-ky-thuat-web-60c98c7f3f07bf0a5af5b6de)

![Đặng Hàm Ý](https://cdn.helpex.vn/upload/2016/09/29/55ee433f8b75f32e34155d4f-074002794_50x50.jpg)[Đặng Hàm Ý](https://helpex.vn/thanh-vien/55ee433f8b75f32e34155d4f)·17:00 25/09/201622 giờ trước

_tldr; tìm hiểu cách sử dụng bảng điều khiển dành cho nhà phát triển và các lệnh javascript cơ bản (cho, do… trong khi, nếu… sau đó, các biến) để tự động hóa theo gui và tăng cường thử nghiệm web kỹ thuật tương tác của bạn._

[![Cách sử dụng bảng điều khiển JavaScript để kiểm tra kỹ thuật web](https://cdn.helpex.vn/upload/2016/09/25/05-00-00-000-43d8d1db-6a2e-45f2-9494-7067a8e26f38.jpg)](http://www.eviltester.com/images/blog/browsers/jscheat/usejsconsolepromo_512x288.jpg)

trước đây tôi chỉ sử dụng bảng điều khiển javascript trong các công cụ dành cho nhà phát triển chrome để xem các lỗi javascript. nhưng tôi đã đọc tài liệu về [bảng điều khiển javascript ](https://developers.google.com/web/tools/chrome-devtools/debug/console/?hl=en)và nhận ra rằng tôi có thể [ra lệnh ](https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference?hl=en)và xem các đối tượng. tôi dần dần nhận ra rằng tôi có thể sử dụng điều này để giúp tôi kiểm tra tốt hơn.

### chơi với các trò chơi javascript để học

và sau đó tôi bắt đầu chơi với các trò chơi javascript… ý tôi là 'chơi với' chứ không phải 'chơi'. về cơ bản là 'hack' các trò chơi để có được mạng sống vô hạn. tôi bắt đầu làm điều đó bằng cách hiểu mã, chặn mã nguồn trong proxy và thay đổi nó. tôi đã viết blog về ' [trò chơi hack ](http://blog.eviltester.com/2016/09/hacking-javascript-games-to-improve.html)'.

tất cả những điều này tôi đã làm bởi vì tôi đã học đủ javascript để trở nên nguy hiểm và cách viết nó trên bảng điều khiển.

### nhưng người thử nghiệm 'làm việc' với các ứng dụng

với tư cách là người thử nghiệm, tôi muốn tương tác với các ứng dụng, vì vậy tôi thực sự muốn học cách kiểm tra các ứng dụng trong điều kiện và khám phá nhiều điều kiện hơn.

vì vậy, thay vì phải nhấp vào nút 'tôi muốn một khẩu hiệu' trong "bộ tổ chức [thử nghiệm ác ](http://eviltester.com/apps/sloganizer.html)" của tôi , mọi lúc. thay vào đó, tôi có thể tạo hàng nghìn khẩu hiệu từ bảng điều khiển và xem lại chúng ở đó.

ví dụ

```
for(var x=0;x<100;x++){console.log("-" + random_sentence());}
```

hoặc tôi có thể chỉnh sửa mã một chút để tạo ra hàng nghìn bản thuyết minh của một mẫu cụ thể. rất hữu ích khi thử nghiệm thứ gì đó được tạo ngẫu nhiên.

bạn có thể thử cho mình trên [sloganizer ](http://eviltester.com/apps/sloganizer.html), mã khá đơn giản của nó, xem liệu bạn có thể tìm ra cách tạo 1000 bản thuyết minh của một câu cụ thể hay không.

nếu tôi đang thử nghiệm ứng dụng [todomvc ](http://todomvc.com/examples/backbone/), thì tôi sẽ muốn tạo nhiều dữ liệu rất dễ dàng, thay vì phải tạo một loạt các mục cần làm trong ứng dụng này một cách thủ công và chậm chạp. tôi có thể viết một lượng nhỏ mã và tạo 100 mã trong vòng chưa đầy một giây. hoặc 1000 trong vòng chưa đầy một giây. hoặc thực sự thúc đẩy ứng dụng và xem nó có thể đối phó với bao nhiêu ứng dụng.

nhưng tôi cũng có thể điều khiển chúng - giả sử chuyển đổi một nửa trong số chúng. và tôi cũng có thể xóa chúng.

hãy thử nó cho chính mình:

* xem bạn có thể tạo các việc cần làm tự động không
* xóa việc cần làm
* sửa đổi chúng
* chuyển đổi chúng để được hoàn thành

nếu bạn có thể làm như trên thì bạn đã triển khai các chức năng crud (tạo, đọc, cập nhật, xóa) cần thiết để tăng cường khả năng khám phá và thử nghiệm tương tác của bạn đối với ứng dụng và bây giờ bạn sẽ có thể kiểm tra toàn bộ các kết hợp và điều kiện mới điều đó sẽ khó kiểm tra hơn.

### từ bảng điều khiển

và tất cả những điều này chúng ta có thể làm từ bảng điều khiển javascript.

với một số lệnh javascript cơ bản:

* [biến ](http://www.w3schools.com/js/js_variables.asp),
* [nếu… thì ](http://www.w3schools.com/js/js_if_else.asp),
* [cho các vòng lặp ](http://www.w3schools.com/js/js_loop_for.asp),
* [trong khi ](http://www.w3schools.com/js/js_loop_while.asp)các vòng lặp và [do ... while ](http://www.w3schools.com/jsref/jsref_dowhile.asp)vòng

chúng ta cũng cần học cách tìm các đường nối trong ứng dụng javascript bằng cách đọc nguồn và sử dụng chức năng 'tìm'.

### học cách sử dụng bảng điều khiển

bảng điều khiển là một công cụ mà tôi hầu như không bao giờ sử dụng trong thử nghiệm của mình. tôi chủ yếu làm việc với các ứng dụng web ở cấp độ gui hoặc http.

bằng cách sử dụng bảng điều khiển, chúng tôi có thể làm việc trong gui mà không cần phải rời khỏi trình duyệt và thử nghiệm ở cấp độ mới với ứng dụng của chúng tôi.

điều này sẽ càng trở nên quan trọng hơn khi chúng ta phải làm việc với nhiều ứng dụng trang đơn hơn và các ứng dụng sử dụng khung và mã javascript mvc sử dụng dom bóng.

nếu bạn đã học html và css và bạn biết cách sử dụng proxy, thì bạn có thể đẩy thử nghiệm web kỹ thuật của mình xuống cấp javascript và học cách tận dụng bảng điều khiển trong quá trình thử nghiệm của mình.

[cách kiểm tra ứng dụng javascript từ bảng điều khiển công cụ dành](https://vimeo.com/183799777) cho [nhà phát triển chrome ](https://vimeo.com/183799777)trên vimeo.

bạn có thể [xem video này trên youtub ](https://www.youtube.com/watch?v=ypwto1g6dic)e nếu bạn thích.

**Ghi chú:**

phần “kiểm tra javascript từ bảng điều khiển công cụ dành cho nhà phát triển trình duyệt” trong khóa học “kiểm tra web kỹ thuật 101” của tôi hướng dẫn bạn cách thực hiện việc này. phần này có hơn một giờ video giải thích cách kiểm tra các ứng dụng javascript từ bảng điều khiển trình duyệt. [kiểm tra khóa học ](http://compendiumdev.co.uk/page.php?title=techweb101course)và chỉ với $ 10, bạn có thể tìm hiểu về các công cụ dành cho nhà phát triển, proxy, thử nghiệm nghỉ ngơi, thử nghiệm web di động và hơn thế nữa.

* [javascript](https://helpex.vn/it-code/cau-hoi-chu-de/javascript)
* [console](https://helpex.vn/it-code/cau-hoi-chu-de/console)
* [testing](https://helpex.vn/it-code/cau-hoi-chu-de/testing)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)

15 hữu ích0 bình luận3.5k xemchia sẻ

## [Angular 2: Sử dụng JSONPipe để gỡ lỗi](https://helpex.vn/article/angular-2-su-dung-jsonpipe-de-go-loi-60c98c763f07bf0a5af5b6dd)

![Bùi Sơn Tùng](https://cdn.helpex.vn/upload/2015/05/06/55ee434a8b75f32e34156a46-030315886_50x50.jpg)[Bùi Sơn Tùng](https://helpex.vn/thanh-vien/55ee434a8b75f32e34156a46)·17:00 25/09/20166 ngày trước

một cách mạnh mẽ để gỡ lỗi (đặc biệt là các mẫu) trong góc 1 là json pipe (hoặc bộ lọc) có thể được sử dụng trong một mẫu. đường ống vẫn tồn tại nguyên bản trong góc 2. đây là cách bạn có thể nhập và sử dụng nó.

đặc biệt là khi bạn phải gỡ lỗi các mẫu góc của mình, nó đặc biệt hữu ích trong góc 1 để sử dụng bộ lọc json.

![Angular 2: Sử dụng JSONPipe để gỡ lỗi](https://cdn.helpex.vn/upload/2016/09/25/05-00-00-000-7d48284f-1559-471c-9590-8e2e8f3c3473.jpg)

```
<h1>some template</h1><pre>{{ myobj | json }}</pre>
```

kết quả là bạn có được một biểu diễn json được định dạng độc đáo cho đối tượng javascript kết hợp dữ liệu của bạn.

![Angular 2: Sử dụng JSONPipe để gỡ lỗi](https://cdn.helpex.vn/upload/2016/09/25/05-00-00-000-fee872f9-dd1c-4e8a-bfb5-9f4e35774084.jpg)

### sử dụng jsonpipe trong góc 2

điều tương tự đối với góc 2, cũng có [đối tượng jsonpipe tích ](https://angular.io/docs/ts/latest/api/common/index/jsonpipe-pipe.html)hợp.

để sử dụng nó, bạn phải nhập gói `commonmodule` từ `@angular/common` gói vào mô-đun của riêng bạn.

```
import { commonmodule } from '@angular/common';  @ngmodule({    ...    imports: [ commonmodule ]    ...})
```

thì bạn có thể bắt đầu sử dụng nó trong mẫu của mình, giống như bạn đã làm trong góc 1.

```
@component({    selector: 'my-app',    template: `      <pre>{{ myobj | json }}</pre>    `})export class myappcomponent {    myobj: any;     constructor() {        this.myobj = {            name: 'juri',            website: 'http://juristr.com',            twitter: '@juristr'        };    }}
```

dễ dàng, phải không?

### thử nó cho mình

đây là một plunker để chơi với: [https://plnkr.co/edit/za3ogwlgwg0ralyz1ivj?p=preview](https://plnkr.co/edit/za3ogwlgwg0ralyz1ivj?p=preview)

* [json](https://helpex.vn/it-code/cau-hoi-chu-de/json)
* [angularjs](https://helpex.vn/it-code/cau-hoi-chu-de/angularjs)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)

1 hữu ích0 bình luận3.1k xemchia sẻ

## [Hiểu về phát hiện thay đổi Angular 2](https://helpex.vn/article/hieu-ve-phat-hien-thay-doi-angular-2-60c98c6b3f07bf0a5af5b6dc)

![Huỳnh Quốc Hùng](https://cdn.helpex.vn/upload/2018/10/15/55ee43058b75f32e3415150b-045354210_50x50.jpg)[Huỳnh Quốc Hùng](https://helpex.vn/thanh-vien/55ee43058b75f32e3415150b)·17:00 25/09/2016hôm qua

**TL; DR** Angular 2 giới thiệu một hệ thống phát hiện thay đổi được sáng tạo lại giúp giảm các chu kỳ thông báo theo hướng một chiều. Ngoài ra, tính năng phát hiện thay đổi giờ đây có thể được kiểm soát và tinh chỉnh bởi các nhà phát triển để tận dụng tối đa hiệu suất của khung.

### Giới thiệu về Phát hiện Thay đổi

Angular 2 cuối cùng đã được phát hành. Bạn có thể đã nghe nói về một số thay đổi do va chạm phiên bản lớn: nó đã được viết lại hoàn toàn, TypeScript được chọn làm ngôn ngữ lựa chọn, các biểu mẫu được phát minh lại, RxJS, một bộ định tuyến hoàn toàn mới, v.v. Theo ý kiến ​​của tôi, hầu hết điều đáng giá là việc thiết kế lại hệ thống phát hiện thay đổi cốt lõi. Như bạn có thể nhớ, hiệu suất vòng lặp thông báo của AngularJS (hay còn gọi là Angular 1) có vấn đề. Bây giờ thì không.

#### Tại sao chúng ta cần phát hiện thay đổi?

Quan tâm làm gì? Nói chung, sức mạnh của các khung JavaScript hiện đại hoạt động giống như sau: một sự kiện thay đổi trong mô hình và buộc phải thay đổi giao diện người dùng. Đây là phát hiện thay đổi, hệ thống giám sát các sự kiện và hành động trên chúng. Một cái gì đó phải kích hoạt sự lan truyền này đến chế độ xem. Như đã đề cập trước đây, trong Angular 1, chúng tôi có các vòng lặp thông báo kiểm tra mọi tham chiếu đơn lẻ được đặt để theo dõi các thay đổi giá trị. Khi Angular phát hiện ra rằng mọi thứ đều ổn định (không có vòng lặp vô hạn, v.v.), nó đã tuyên truyền các thay đổi đối với chế độ xem. Mặc dù điều này không hiệu quả, nhưng nó đã hoạt động trong một thời gian dài. Ngoài ra, vấn đề là theo dõi các sự kiện không đồng bộ. Bạn cũng có thể sử dụng `$scope.$apply(...)`nếu bạn đã làm việc với Angular 1. Để hiểu tại sao nó lại cần thiết, chúng ta hãy bắt đầu lại từ đầu.

#### Cách hoạt động của Javascript

Thời gian chạy JavaScript hoạt động trên một công cụ phân luồng duy nhất. Bạn có thể đã nghe nói về ngăn xếp (có thể từ các ngôn ngữ lập trình khác). Hãy lấy đoạn mã dưới đây:

```
console.log('Hey')setTimeout(() => {   console.log('Hello from timeout!')}, 1000);console.log('Hi')
```

Chúng tôi sẽ thấy điều này trong một bảng điều khiển dưới dạng:

```
HeyHiHello from timeout!
```

Hơn nữa, không có gì bị chặn trong khoảng thời gian chờ một giây. Vậy công cụ JS sẽ thực hiện điều này như thế nào với một luồng duy nhất?

**Mã đồng bộ**

Hãy đi từng bước một. Nếu bạn có mã như thế này:

```
console.log('1')console.log('2')console.log('3')
```

mọi lệnh sẽ được đưa vào ngăn xếp và sẽ chạy từng lệnh một. Không có khả năng nhìn thấy 3 trước 2 hoặc 1. Vì vậy, chúng tôi sẽ kết thúc với những điều sau:

```
123
```

Mỗi lần. Mọi nơi.

**Mã không đồng bộ**

Nhưng hãy quay lại thời gian chờ:

```
console.log('1')setTimeout(() => {  console.log('2')}, 0)console.log('3')
```

Điều gì xảy ra bây giờ? Trên ngăn xếp, chúng ta sẽ có:

```
console.logsetTimeoutconsole.log
```

Bí quyết ở đây là cách thức `setTimeout`hoạt động và nó thực sự là gì. Có, nó sẽ được gọi như một hành động đồng bộ bình thường, nhưng tất cả những gì mà công cụ JS làm là đưa bánh xe cho một thứ khác. Có một loạt các API trình duyệt không phải là một phần của quy trình đơn luồng này. Và có một thứ gọi là vòng lặp sự kiện. Vòng lặp sự kiện này lần lượt đi qua các hướng dẫn ngăn xếp và nếu nó trống, thì nó sẽ chuyển đến _hàng đợi gọi lại_ . Tham chiếu đến `setTimeout`mã là ở đó. Sau khi gọi lại xong, mã sẽ chuyển đến ngăn xếp.

Nó có nghĩa là gì? Hai điều:

* Mọi thứ bên trong lệnh gọi lại không đồng bộ (như trong `setTimeout`) sẽ được chạy _sau_ bất kỳ mã đồng bộ nào khác; đây là lý do tại sao hack `setTimeout(() => {}, 0)`hoạt động như vậy .
* Chúng tôi không có cách nào để đảm bảo 1000ms _chính xác_ là 1000ms (nhưng chúng tôi biết rằng nó ít nhất là 1000ms).

Để hiểu đầy đủ về vòng lặp sự kiện và những gì đang diễn ra trong trình duyệt, tôi khuyến khích bạn xem qua bài [nói chuyện](https://www.youtube.com/watch?v=8aGhZQkoFbQ) này của [Philip Roberts](https://www.youtube.com/watch?v=8aGhZQkoFbQ) .

#### Cách các khu vực liên quan đến phát hiện thay đổi

Làm thế nào để tất cả những điều này liên quan đến Angular và phát hiện thay đổi? Theo dõi các đối tượng với mã đồng bộ là khá dễ dàng. Tuy nhiên, khi nói đến mã không đồng bộ, mọi thứ trở nên phức tạp. Đó là bởi vì góc 1 buộc chúng tôi phải sử dụng `$scope.$apply(...)`mỗi lần một hành động không đồng bộ đã được thực hiện hoặc sử dụng các đường góc làm những hành động không đồng bộ: `$timeout`, `$http`, và vân vân. Vấn đề là, nếu một cái gì đó được thực hiện bên ngoài bộ điều khiển (thậm chí là một thay đổi hoàn toàn hợp lệ đối với đối tượng tham chiếu), Angular không biết về nó, vì vậy nó không kích hoạt bất kỳ sự kiện nào để phản ánh các thay đổi đối với giao diện người dùng.

Mặt khác, bây giờ chúng ta có Angular 2. Nó đã loại bỏ tất cả những thứ được kết nối với các chu kỳ tiêu hóa và bây giờ sử dụng _Zones_ . Các khu vực có thể theo dõi ngữ cảnh của các hành động không đồng bộ bằng cách vá chúng (tức là ghi đè chúng bằng mã riêng của nó), sau đó gọi hành động mong muốn nhưng kèm theo một số thông tin bổ sung. Thông tin bổ sung này là bối cảnh. Bằng cách này, Angular sẽ biết hành động không đồng bộ được gọi từ thành phần nào.

Chiến thắng lớn của phương pháp này là chúng ta có thể sử dụng các API của trình duyệt một cách nguyên bản và Angular sẽ biết điều gì đang xảy ra mà không buộc chúng ta phải thông báo thủ công về việc thay đổi đã xảy ra. Hạn chế là Zones ghi đè các hành động không đồng bộ, đây là một loại giải pháp khó hiểu và có thể ảnh hưởng đến mã (hiện có) khác nếu chúng tôi không chỉ dựa vào Angular trong ứng dụng.

Nhưng chính xác thì Angular được thông báo về sự thay đổi như thế nào? Angular sử dụng phiên bản Zone được gọi của riêng nó `NgZone`, nó chuyển tiếp các hành động không đồng bộ đã hoàn thành với `onTurnDone`sự kiện. Phát hiện thay đổi góc đợi sự kiện thực hiện phát hiện thay đổi và kiểm tra những gì cần được cập nhật trong giao diện người dùng. Đó là hành vi cốt lõi.

### Tận dụng tính năng phát hiện thay đổi trong ứng dụng của bạn

Mọi thứ được mô tả ở trên đang diễn ra dưới mui xe. Điều quan trọng không kém là chúng ta có thể tận dụng nó như thế nào. Không giống như Angular 1, Angular 2 cho chúng ta khả năng kiểm soát phát hiện thay đổi. Tuy nhiên, nhóm Angular tuyên bố rằng ngay cả khi không có bất kỳ điều chỉnh hiệu suất nào, nó nhanh hơn 3 đến 10 lần so với trước đó và đối với hầu hết các ứng dụng, điều này sẽ đủ nhanh. Nhưng nó có thể nhanh hơn nhiều. Hãy xem một ví dụ.

```
  <head>    <base href="." />    <title>angular2 playground</title>    <link rel="stylesheet" href="style.css" />    <script src="https://unpkg.com/zone.js@0.6.21/dist/zone.js"></script>    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>    <script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>    <script src="config.js"></script>    <script>    System.import('app')      .catch(console.error.bind(console));  </script>  </head>   <body>    <my-app>      loading...    </my-app>  </body> </html>
```

Đây là một vấn đề rất điển hình: hiển thị một danh sách. Có một thành phần chứa danh sách các thành phần khác có một số dữ liệu đầu vào. Nói chung, chúng ta có một vùng chứa với dữ liệu và một thành phần câm chỉ để hiển thị một mục danh sách duy nhất. Không có gì lạ mắt ở đây, chỉ là cái và `ngOnChange`. Những gì đang được thực hiện ở đây? `ngOnChange`phản ứng trên mọi thay đổi đầu vào và getter thêm ghi nhật ký bổ sung mỗi lần `rowData`được tìm nạp. Lưu ý rằng chúng tôi không sử dụng nó ở bất kỳ đâu bên ngoài mẫu.

Điều này có nghĩa là getter được kích hoạt bởi chính Angular. Và đoán xem điều gì sẽ xảy ra? Chúng tôi có một thay đổi duy nhất trên đầu vào, nhưng có hàng trăm nhật ký getter ở đó.

Tại sao vậy?

Angular được thông báo về sự thay đổi từ một số thành phần và phải kiểm tra xem điều đó ảnh hưởng như thế nào đến trạng thái hiện tại, vì vậy nó sẽ kiểm tra tất cả các giá trị cho sự thay đổi. Trên thực tế, nhóm cho biết họ có thể thực hiện hàng nghìn lần kiểm tra như vậy trong mili giây, nhưng nó vẫn lãng phí thời gian và thậm chí có thể gây hại cho ứng dụng dựa trên dữ liệu lớn của chúng tôi.

#### Tính bất biến

Điều thú vị về hệ thống phát hiện thay đổi mới là giờ đây chúng ta có thể điều chỉnh nó. Hãy tạm dừng Angular và xem xét đoạn mã sau:

```
const users = [{  name: 'John',  age: 27}, {  name: 'Anna',  age: 23}] users.push({  name: 'Max',  age: 30})
```

Điều quan trọng nhất ở đây là `const`khai báo. Nếu `users`là hằng số, chúng ta có thể sửa đổi nó như thế nào? Đó là cách JavaScript hoạt động! Điều này `const`ngăn chúng tôi sửa đổi một tham chiếu đến đối tượng cụ thể trong JavaScript. Những gì `push`phương thức `Array`thực sự đang làm là thêm một đối tượng khác vào mảng hiện có (không có thay đổi tham chiếu). Hãy đi đến một ví dụ rất điển hình khác:

```
const user = {  name: 'Max',  age: 30} user.age = 31
```

Điều tương tự cũng được áp dụng. Mặc dù chúng ta không thể sửa đổi toàn bộ đối tượng để biến nó thành một đối tượng khác (thay đổi tham chiếu), chúng ta vẫn có thể thay đổi một phần của đối tượng!

Đây là lý do tại sao các kiểm tra mà chúng ta đã thảo luận trước đây không tốt như vậy. Nếu bạn muốn kiểm tra xem đối tượng có giống như trước đây hay không, bạn phải _kiểm tra sâu tất cả các thuộc tính của nó_ . Nó không hiệu quả.

Làm thế nào chúng ta có thể buộc đối tượng phải là đối tượng mới với thuộc tính đã thay đổi? Nó thực sự khá dễ dàng với [đề xuất thuộc tính trải rộng Đối tượng ECMAScript](https://github.com/sebmarkbage/ecmascript-rest-spread) mới :

```
const user = {  name: 'Max',  age: 30} const modifiedUser = { ...user, age: 31 }
```

#### Thay đổi chiến lược phát hiện

Phần tốt của tất cả những điều này là bây giờ chúng ta có thể nói với Angular rằng _chúng ta biết những gì chúng ta đang làm_ . Để thay đổi hành vi phát hiện sự thay đổi, chúng ta có thể sử dụng `ChangeDetectionStrategy`API, trong đó có một giá trị rất thú vị: `OnPush`. Nó làm cho một thành phần được áp dụng chiến lược này chỉ nhìn vào các giá trị bên trong khi tham chiếu trên đầu vào thay đổi hoặc một số sự kiện đã được kích hoạt khỏi thành phần.

Hãy thêm `OnPush`chiến lược vào ví dụ trước của chúng tôi:

```
import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; @Component({  selector: 'row',  template: `    <pre></pre>  `,  changeDetection: ChangeDetectionStrategy.OnPush})export class RowComponent {  ...}
```

Bạn có thể thử đoạn mã dưới đây trên [Plunker](http://plnkr.co/) và xem sự khác biệt.

```
<!DOCTYPE html><html>   <head>    <base href="." />    <title>angular2 playground</title>    <link rel="stylesheet" href="style.css" />    <script src="https://unpkg.com/zone.js@0.6.21/dist/zone.js"></script>    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>    <script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>    <script src="config.js"></script>    <script>    System.import('app')      .catch(console.error.bind(console));  </script>  </head>   <body>    <my-app>      loading...    </my-app>  </body> </html>
```

Cải tiến lớn là giờ đây chỉ có một cuộc gọi nhận cho một thay đổi! Chúng tôi không cần thêm gì nữa vì dữ liệu đầu vào của chúng tôi là các chuỗi đang được thay đổi, do đó tham chiếu về đầu vào sẽ thay đổi. Tham chiếu cho phần còn lại của các thành phần không thay đổi, vì vậy Angular thậm chí không nhìn vào nó.

#### Cấu trúc ứng dụng

Làm cách nào để chúng tôi có thể xây dựng một ứng dụng hoạt động hiệu quả? Với Angular 2, nó thực sự khá dễ dàng. Như trong tất cả các khung công tác thành phần ngày nay, bạn nên có các thành phần thông minh và ngu ngốc. Các thành phần câm, vốn chỉ dùng để hiển thị dữ liệu từ đầu vào hoặc xử lý các sự kiện của người dùng, là những tình nguyện viên lý tưởng để có `OnPush`chiến lược. Các thành phần thông minh đôi khi sẽ yêu cầu bạn theo dõi nhiều thứ hơn đầu vào và các sự kiện, vì vậy hãy cẩn thận với việc thiết lập `OnPush`chiến lược ở đó.

### Bên cạnh: Sử dụng Angular 2 với Auth0

Auth0 phát hành Mã thông báo **web JSON** trên mỗi lần đăng nhập cho người dùng của bạn. Điều đó có nghĩa là bạn có thể có một cơ sở hạ tầng nhận dạng vững chắc, bao gồm đăng nhập một lần, quản lý người dùng, hỗ trợ cho mạng xã hội (Facebook, Github, Twitter, v.v.), doanh nghiệp (Active Directory, LDAP, SAML, v.v.) và cơ sở dữ liệu của riêng bạn của người dùng chỉ với một vài dòng mã.

Bạn có thể thêm Auth0 vào ứng dụng Angular 2 rất dễ dàng. Chỉ có một số bước đơn giản:

#### Bước 0: Đăng ký Auth0 và định cấu hình

Nếu bạn chưa có bất kỳ tài khoản Auth0 nào, hãy [đăng ký](https://auth0.com/signup) một tài khoản ngay bây giờ để làm theo các bước khác.

#### Bước 1: Thêm Auth0lock vào ứng dụng của bạn

[Khóa](https://auth0.com/lock) là tiện ích hộp đăng nhập tuyệt đẹp (và hoàn toàn có thể tùy chỉnh) đi kèm với Auth0. Tập lệnh cho nó có thể được đưa vào từ liên kết CDN hoặc bằng npm.

Lưu ý: Nếu bạn sử dụng npm để tải Auth0Lock, bạn sẽ cần đưa nó vào bước xây dựng của mình.

```
   <!-- src/client/index.html -->   ...   <!-- Auth0 Lock script -->  <script src=“https://cdn.auth0.com/js/lock/10.0/lock.min.js"></script>   <!-- Setting the right viewport -->  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />   ...
```

#### Bước 2: Thêm dịch vụ xác thực

Tốt nhất bạn nên thiết lập một dịch vụ có thể tiêm để xác thực có thể được sử dụng trên toàn ứng dụng.

Với Auth0, chúng tôi có quyền truy cập vào hồ sơ của người dùng và JWT trong lệnh `lock.on`gọi lại nơi chúng tôi lắng nghe `authenticated`sự kiện được kích hoạt khi người dùng đăng nhập thành công và các mục này có thể được lưu trong bộ nhớ cục bộ để sử dụng sau này.

```
// src/client/shared/auth.service.ts import {Injectable, NgZone} from 'angular2/core';import {Router} from 'angular2/router';import {AuthHttp, tokenNotExpired} from 'angular2-jwt'; // Avoid name not found warningsdeclare var Auth0Lock: any; @Injectable()export class AuthService {  lock = new Auth0Lock('YOUR_AUTH0_CLIENT_ID', 'YOUR_AUTH0_DOMAIN');  refreshSubscription: any;  user: Object;  zoneImpl: NgZone;   constructor(private authHttp: AuthHttp, zone: NgZone, private router: Router) {    this.zoneImpl = zone;    this.user = JSON.parse(localStorage.getItem('profile'));     // Add callback for lock `authenticated` event    var self = this;    this.lock.on("authenticated", authResult => {      self.lock.getProfile(authResult.idToken, (error, profile) => {         if (error) {          // handle error          return;        }         // If authentication is successful, save the items        // in local storage        localStorage.setItem('profile', JSON.stringify(profile));        localStorage.setItem('id_token', authResult.idToken);        self.zoneImpl.run(() => self.user = profile);      });    });  }   public authenticated() {    // Check if there's an unexpired JWT    return tokenNotExpired();  }   public login() {    // Show the Auth0 Lock widget    this.lock.show();  }   public logout() {    localStorage.removeItem('profile');    localStorage.removeItem('id_token');    this.zoneImpl.run(() => this.user = null);    this.router.navigate(['Home']);  }}
```

#### Bước 3: Thêm Trình xử lý nhấp chuột để đăng nhập

Để có thể sử dụng phương thức "Đăng nhập" và "Đăng xuất", chúng ta cần đưa dịch vụ Auth vào `app.components.ts`tệp.

```
 <!-- src/client/app.components.ts -->import { Component } from '@angular/core';import { AuthService } from './auth.service'; ...
```

Trong NgModule gốc của bạn, hãy khai báo nhà cung cấp dịch vụ, như được hiển thị trong đoạn mã sau.

```
 <!-- src/client/app.module.ts --> ...import { AuthService } from './auth.service'; @NgModule({  imports: [    BrowserModule,    FormsModule,    routing,    HttpModule  ],  declarations: [    AppComponent  ],  providers: [    AUTH_PROVIDERS,    AuthService  ],  bootstrap: [AppComponent]})export class AppModule { }
```

Bây giờ, chúng tôi có thể sử dụng các phương pháp từ dịch vụ xác thực của chúng tôi trong bất kỳ thành phần nào của chúng tôi, có nghĩa là chúng tôi có thể dễ dàng thêm trình xử lý nhấp chuột vào nút "Đăng nhập" và "Đăng xuất".

```
  <!-- src/client/app.component.ts -->   ...   <button (click)="authService.login()" *ngIf="!authService.authenticated()">Log In</button>  <button (click)="authService.logout()" *ngIf="authService.authenticated()">Log Out</button>   ...
```

Sau khi người dùng đăng nhập, [Mã thông báo web JSON](https://jwt.io/introduction) sẽ được lưu cho họ trong bộ nhớ cục bộ. JWT này sau đó có thể được sử dụng để thực hiện các yêu cầu HTTP đã xác thực tới một API.

#### Bước 5: Thực hiện các yêu cầu HTTP đã xác thực

Với [**anuglar2-jwt**](https://github.com/auth0/angular2-jwt) , chúng tôi có thể tự động gửi [**các JWT**](https://github.com/auth0/angular2-jwt) của mình trong các yêu cầu HTTP. Để làm như vậy, chúng ta cần phải tiêm và sử dụng `AuthHttp`.

```
// src/client/ping/ping.component.ts import {Component} from 'angular2/core';import {Http} from 'angular2/http'; import {AuthHttp} from 'angular2-jwt';import {Auth} from './auth.service';import 'rxjs/add/operator/map'; @Component({  selector: 'ping',  template: `    <h1>Send a Ping to the Server</h1>    <button class="btn btn-primary" (click)="securedPing()" *ngIf="auth.authenticated()">Secured Ping</button>    <h2></h2>  `})export class Ping {  API_URL: string = 'http://localhost:3001';  message: string;   constructor(private http: Http, private authHttp: AuthHttp, private auth: Auth) {}   securedPing() {    this.authHttp.get(`${this.API_URL}/secured/ping`)      .map(res => res.json())      .subscribe(        data => this.message= data.text,        error => this.message = error._body      );  }}
```

#### Bước 5: Đã xong!

Đó là tất cả những gì cần làm để thêm xác thực vào ứng dụng Angular 2 của bạn với Auth0!

### Kết luận

#### Ưu điểm về hiệu suất

Một trong những lợi thế lớn của việc sử dụng tính năng phát hiện thay đổi chặt chẽ hơn là tăng hiệu suất. Angular được sử dụng cho các ứng dụng lớn có thể xử lý nhiều dữ liệu động. Nhóm Angular đã cung cấp cho nhà phát triển các công cụ cần thiết để tinh chỉnh và cải thiện hiệu suất ngay từ đầu. Theo mặc định, mọi thay đổi sẽ được phản ánh trên giao diện người dùng, vì Angular sẽ xử lý điều đó, nhưng giá là hiệu suất thấp hơn. Mã bất biến hoặc mã phản ứng khó viết hơn nhưng dễ bảo trì và lập luận hơn. Sự lựa chọn là của bạn.

#### Cuối cùng thì Angular có thể được tinh chỉnh

Điều tốt là chúng tôi có quyền lựa chọn với Angular 2. Trong Angular 1, không thể thoát khỏi chu trình tiêu hóa. Ở một số điểm, việc sử dụng React hoặc một thư viện khác để kết xuất giao diện người dùng thay vì các mẫu Angular là một điều thuận lợi, vì nó quá chậm khi xử lý một lượng lớn dữ liệu động. Bây giờ, bạn có một giải pháp hoàn chỉnh với nhiều quyền kiểm soát hơn đối với hành vi nội bộ. Điều này, kết hợp với những thay đổi khác được thực hiện cho Angular 2, làm cho đường cong học tập của khung công tác dốc hơn nhưng đáng giá.

* [angular 2](https://helpex.vn/it-code/cau-hoi-chu-de/angular-2)
* [javascript](https://helpex.vn/it-code/cau-hoi-chu-de/javascript)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)

20 hữu ích0 bình luận15k xemchia sẻ

## [Tạo thành phần vùng chứa trong Angular 2 \[Đoạn mã\]](https://helpex.vn/article/tao-thanh-phan-vung-chua-trong-angular-2-doan-ma-60c98bc93f07bf0a5af5b6d3)

![Nguyễn Dạ Bích](https://cdn.helpex.vn/upload/2015/04/08/55ee42ca8b75f32e3414ca24-055831261_50x50.jpg)[Nguyễn Dạ Bích](https://helpex.vn/thanh-vien/55ee42ca8b75f32e3414ca24)·17:00 28/09/20162 giờ trước

Trong một trong những dự án cuối cùng, tôi cần một thành phần có thể tái sử dụng được chia sẻ, thành phần này cần được mở rộng với nội dung hoặc chức năng bổ sung bởi chế độ xem người sử dụng thành phần này. Trong trường hợp của chúng tôi, nó là một loại thanh menu được sử dụng bởi nhiều chế độ xem. (Chế độ xem, trong trường hợp này, có nghĩa là các mục tiêu định tuyến.)

Việc tạo các thành phần như vậy dễ dàng hơn mong đợi. Dù sao thì tôi cũng đã dành gần cả ngày để tìm ra giải pháp đó, tôi đã chơi với các nhà cung cấp chế độ xem và mẫu, cố gắng truy cập vào mẫu và thao tác với mẫu. Tôi cũng đã cố gắng tạo một chỉ thị cấu trúc của riêng mình.

Nhưng bạn chỉ cần sử dụng chỉ thị trong thành phần vùng chứa.

```
<nav>  <div class="navigation pull-left">    <ul>      <!-- the menu items --->    </ul>  </div>  <div class="pull-right">    <ng-content></ng-content>  </div></nav
```

Đó là tất cả. Bạn không cần phải viết bất kỳ mã TypeScript nào để làm việc này. Sử dụng thành phần này bây giờ khá trực quan:

```
<div class="nav-bar">  <app-navigation>    <button (click)="printDraft($event)">print draft</button>    <button (click)="openPreview($event)">Show preview</button>  </app-navigation></div>
```

Nội dung của - các nút - bây giờ sẽ được đặt vào trình giữ chỗ.

Sau khi dành gần cả ngày để làm việc này, câu hỏi đầu tiên của tôi là: Nó có thực sự dễ dàng như vậy không? Vâng, đúng vậy. Đó là tất cả.

Có thể bạn đã biết về nó. Nhưng tôi không thể tìm thấy bất kỳ gợi ý nào trong tài liệu, trên StackOverflow hoặc trong bất kỳ Blog nào về nó. Có thể yêu cầu này không được sử dụng thường xuyên. Ít nhất tôi đã tình cờ tìm thấy [một tài liệu mà ng-content được sử dụng](https://angular.io/docs/ts/latest/guide/component-styles.html#!#loading-styles) và tôi quyết định viết về nó. Hy vọng nó sẽ giúp ích cho người khác.

* [typescript](https://helpex.vn/it-code/cau-hoi-chu-de/typescript)
* [angular 2](https://helpex.vn/it-code/cau-hoi-chu-de/angular-2)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)
* [code snippet](https://helpex.vn/it-code/cau-hoi-chu-de/code-snippet)

3 hữu ích0 bình luận6.4k xemchia sẻ

## [7 điều mà mọi nhà phát triển web phải biết để trở thành một Guru](https://helpex.vn/article/7-dieu-ma-moi-nha-phat-trien-web-phai-biet-de-tro-thanh-mot-guru-60c98b873f07bf0a5af5b6d1)

![Dương Thảo Vân](https://cdn.helpex.vn/upload/2020/01/20/55ee43808b75f32e3415acee-074409430_50x50.jpg)[Dương Thảo Vân](https://helpex.vn/thanh-vien/55ee43808b75f32e3415acee)·17:00 29/09/2016hôm qua

![7 điều mà mọi nhà phát triển web phải biết để trở thành một Guru](https://cdn.helpex.vn/upload/2016/09/29/05-00-00-000-c74296c8-e95f-46ce-a053-51bc53823438.jpg)

Trước đây, nếu bạn biết một chút về mã hóa và thỉnh thoảng có thể làm tài liệu tham khảo về Chiến tranh giữa các vì sao, bạn được coi là một chuyên gia về Nhà phát triển web. Không còn nữa. Sự phát triển web đã chuyển sang xu hướng chủ đạo và số lượng các nhà phát triển ngoài kia đã tăng lên đáng kể.

Điều đó có nghĩa là nếu bạn muốn trở thành một chuyên gia trong lĩnh vực này, bạn cần phải giỏi hơn, hiểu biết hơn và có năng lực hơn các đối thủ cạnh tranh. Tuyệt vời, một số bạn có thể nghĩ, nhưng làm thế nào để bạn làm điều đó? Một câu hỏi hay - một câu hỏi mà chúng tôi sẽ dành phần còn lại của bài viết để khám phá.

### Bạn cần phải đi sâu hơn

Giờ đây, bất kỳ ai có kết nối Internet đều có thể học cách viết mã (có toàn bộ [kênh YouTube dành cho việc này](http://www.etondigital.com/30-youtube-channels-to-learn-programming/) ). Tuy nhiên, bạn cần biết nhiều hơn chúng. Điều này có nghĩa là không ngồi trên vòng nguyệt quế của bạn. Thay vào đó, bạn cần phải thường xuyên trau dồi các kỹ năng của mình, để bạn biết điều gì đang xảy ra, khi nào nó xảy ra và nó ảnh hưởng như thế nào đến những gì bạn đang làm.

Để có thể làm điều này một cách chính xác, bạn cần phải biết nơi để xem xét. Những trang web và tạp chí quan trọng? Chuyên gia nào biết họ đang nói gì và 'chuyên gia' nào không thể tìm ra lỗi trong một dòng mã?

Bạn thấy đấy, đó là nơi bạn có lợi thế hơn so với những người đàn ông ngực khủng. Họ chưa biết nghe ai. Bạn làm. Và điều đó có nghĩa là chi phí duy trì cập nhật sẽ nhỏ hơn nhiều.

### Bạn cần phải rộng hơn

Điều đó nói rằng, việc viết mã một mình không giúp bạn đạt được điều đó - đặc biệt là vì có khá nhiều nền tảng ngoài kia, điều đó có nghĩa là bạn không phải thực sự nhập mã nữa. Họ sẽ làm rất nhiều điều đó cho bạn. Kết quả? Bộ [kỹ năng](http://www.pagepersonnel.co.uk/our-expertise/secretarial-and-business-support/news-and-updates/broaden-your-skill-set-and-boost-your-job-prospects) của bạn [cần được mở rộng](http://www.pagepersonnel.co.uk/our-expertise/secretarial-and-business-support/news-and-updates/broaden-your-skill-set-and-boost-your-job-prospects) . Bạn cần phải trở nên tốt hơn trong cách cư xử với mọi người, bởi vì nếu bạn không làm như vậy, họ sẽ chỉ tự làm điều đó mà thôi.

Và sau đó bạn sẽ đặt bánh mì trên bàn như thế nào?

Vì vậy, bạn không thể chỉ là một chuyên gia về web, bạn còn phải trở thành một chuyên gia về những gì người dùng muốn, cần và mong đợi. Điều đó có nghĩa là hiểu tâm lý người dùng (và khách hàng) để bạn có thể thúc đẩy họ một cách tinh tế theo hướng mong muốn.

### Bạn cần hiểu thiết kế  &#x20;

Bạn viết mã giỏi đến đâu không quan trọng, nếu sản phẩm bạn cung cấp trông giống như phân bò, thì không ai sẽ làm việc với bạn. Và cách duy nhất để bạn có thể tạo ra một thứ gì đó thực sự trông đẹp mắt là ít nhất phải có hiểu biết cơ bản về thiết kế.

Các thanh phải rộng bao nhiêu? Sự kết hợp màu nào là hấp dẫn nhất? Cách bố trí nào là hiệu quả nhất? Mọi người mong đợi gì từ một trang web?

Bạn phải có khả năng trả lời tất cả những câu hỏi này, bởi vì, nếu bạn may mắn, khi bạn nói chuyện với một khách hàng mới, họ sẽ hỏi điều đó. Nếu bạn không may mắn, họ sẽ có những ý tưởng riêng của họ, điều này sẽ không hiệu quả, trong trường hợp này, bạn cần đủ hiểu biết để giải thích cho họ lý do tại sao không nên theo đuổi chúng. Nếu không, bạn có thể chắc chắn rằng họ sẽ đổ lỗi cho bạn vì trang web của họ không hoạt động. Và đó chỉ là những cơn đau đầu xung quanh!

### Bạn cần được bảo mật

Một lĩnh vực khác mà bạn phải biết công cụ của mình là bảo mật, bởi vì nếu công cụ của bạn bị tấn công, thì bạn có thể khá chắc chắn rằng sẽ không ai coi bạn là một chuyên gia lâu hơn nữa.

Nhận ra rằng chỉ biết về bảo mật không có nghĩa là bạn cần phải lập trình tất cả các khía cạnh bảo mật. Trên thực tế, tôi khuyên bạn không nên làm điều đó, bởi vì nếu bạn mắc lỗi trong việc tạo hệ thống bảo mật của riêng mình, rất có thể bạn sẽ không phát hiện ra cho đến khi bạn bị tấn công. Và đến thời điểm đó, đã quá muộn và bạn cũng có thể bắt đầu tìm kiếm một [con đường sự nghiệp mơ ước](http://www.grabmyessay.com/blog/infographics-find-a-major-that-will-put-you-on-the-dream-career-path) khác vì tin tặc vừa đập cửa vào mặt bạn vì [con đường](http://www.grabmyessay.com/blog/infographics-find-a-major-that-will-put-you-on-the-dream-career-path) này.

Thay vào đó, hãy biết ngành đang làm gì và tại sao họ lại làm điều đó. Biết những gì đã được thử nghiệm cho đến chết và vẫn còn đứng. Và hãy đảm bảo rằng bất cứ thứ gì bạn đã phát triển đều được cập nhật!

### Bạn cần hiểu cách tiếp xúc

Ngày nay, xây dựng một trang web là không đủ. Bạn phải chắc chắn rằng nó cũng được phơi bày. Điều này có thể có nhiều hình thức. Ví dụ: bạn có phát triển các trang web của mình để chúng được tối ưu hóa cho cả SEO cũng như phương tiện truyền thông xã hội không?

Bởi vì nếu bạn không làm vậy, thì không ai thực sự sẽ tìm thấy những viên ngọc mà bạn đang đặt ngoài đó. Và nếu không ai nhìn vào họ, thì sẽ không ai gọi bạn là một guru hay đeo bám từng lời nói của bạn.

Vì vậy, hãy đảm bảo rằng nội dung của bạn thực sự nhận được sự hiển thị xứng đáng, bằng cách đảm bảo rằng bạn hiểu các bản cập nhật mới nhất của Google, các thay đổi mới nhất trên mạng xã hội và cách tận dụng chúng theo cách tốt nhất có thể.

### Hiểu những thay đổi sắp tới

[Máy tính sẽ thay đổi](http://www.economist.com/news/leaders/21694528-era-predictable-improvement-computer-hardware-ending-what-comes-next-future) trong những năm tới. Nó vẫn chưa rõ ràng theo hướng nào. Nó có thể là lượng tử, nó có thể là AI, nó có thể là máy học, nó có thể là dữ liệu lớn. Nhưng nó sẽ thay đổi. Và nếu bạn chưa sẵn sàng cho nó, bạn có thể thấy mình đang đi sai hướng của lịch sử.&#x20;

Vì lý do đó, điều quan trọng là bạn phải cập nhật và nhận ra những gì sẽ thay đổi và nó sẽ thay đổi như thế nào để bạn có thể hiểu những gì bạn phải học để duy trì một bước so với thủy triều đang lên của cảnh biển máy tính đang thay đổi . Vì nếu không, bạn có thể thấy rằng lĩnh vực mà bạn là chuyên gia đột nhiên trở nên lỗi thời. Và ai muốn trở thành một guru trong một lĩnh vực mà không ai còn chú ý đến?

### Bạn cần tiếp tục phát triển

Nếu bạn muốn trở thành một nhà phát triển web, bạn phải chấp nhận rằng bạn đang ở một trong những khu vực thay đổi nhanh chóng nhất hiện nay. Và nếu bạn không thay đổi với nó, bạn sẽ nhanh chóng đi theo con đường của loài khủng long.

Vì vậy, hãy tiếp tục học hỏi, tiếp tục phát triển, tiếp tục nhận ra rằng những gì có thể đã hoạt động vài tháng trước có thể không còn đủ tốt cho ngày hôm nay. Không giống như trong các lĩnh vực cũ, nơi mà kiến ​​thức là tĩnh, đó là những gì thực sự cần để trở thành một chuyên gia trong lĩnh vực phát triển web.

Rốt cuộc, bạn có thường xuyên nghe mọi người tham khảo các văn bản từ thế kỷ trước khi họ nói về phát triển web không? Vì vậy, đừng là thế kỷ trước, thậm chí đừng là năm ngoái. Hãy cập nhật và bạn có thể yêu cầu lớp áo của Guruship.&#x20;

* [web developement](https://helpex.vn/it-code/cau-hoi-chu-de/web-developement)
* [tips](https://helpex.vn/it-code/cau-hoi-chu-de/tips)
* [web dev](https://helpex.vn/it-code/cau-hoi-chu-de/web-dev)

1 hữu ích0 bình luận15k xemchia sẻ

## [Giải phẫu của thử nghiệm đơn vị mạnh mẽ (Với các ví dụ trong C #)](https://helpex.vn/article/giai-phau-cua-thu-nghiem-don-vi-manh-me-voi-cac-vi-du-trong-c-%23-60c98b5c3f07bf0a5af5b6d0)

![Phan Ngọc Quyên](https://cdn.helpex.vn/upload/2019/02/05/55ee431b8b75f32e341530ac-015629284_50x50.jpg)[Phan Ngọc Quyên](https://helpex.vn/thanh-vien/55ee431b8b75f32e341530ac)·17:00 30/09/20162 ngày trước

Tôi nghĩ về các bài kiểm tra đơn vị như một phần mở rộng cho mã của tôi. Quá trình kiểm tra kỹ lưỡng mang lại sự an tâm rằng khi cấu trúc lại mã hoặc thực hiện cải tiến hiệu suất, các thiết bị vẫn hoạt động như mong đợi. Nó cũng có thể tìm ra lỗi và các trường hợp cạnh và tránh hồi quy trong quá trình tái cấu trúc.

Tôi đến từ nền tảng .NET / C # và đã biên soạn bộ sưu tập những suy nghĩ và mẩu tin nhỏ này mà tôi thấy hữu ích khi viết bài kiểm tra.

### Tại sao phải viết bài kiểm tra đơn vị?

Tôi thấy rằng [việc viết các bài kiểm tra đơn vị](https://raygun.com/blog/2015/03/designing-your-unit-tests/) và viết mã có thể bảo trì mạnh mẽ thường đi đôi với nhau. Nếu logic bạn đang kiểm tra được trộn lẫn với các thành phần UI, bạn có thể thấy rằng các thành phần cần được tải (có thể trong một chuỗi UI) để các bài kiểm tra có quyền truy cập vào logic. Nếu nhiều lớp được kết hợp chặt chẽ với nhau, thì thiết lập cho một bài kiểm tra đơn vị có thể bị phức tạp. Những ví dụ này và nhiều ví dụ khác về việc kiểm tra đơn vị trở nên khó khăn là một dấu hiệu cho thấy mã có thể được hưởng lợi từ một số cấu trúc lại tốt.

### Phương pháp tiếp cận kiểm thử đơn vị

**Kiểm tra hộp đen** là nơi bạn kiểm tra một đơn vị mà không có quyền truy cập vào mã nguồn của đơn vị hoặc bằng cách từ chối xem xét nó. Để thực hiện kiểm tra hộp đen, bạn có thể biết, được thông báo hoặc thử nghiệm với những gì được mong đợi ở thiết bị.

**Kiểm thử hộp trắng** là nơi bạn kiểm tra mã nguồn của đơn vị được kiểm tra để hỗ trợ viết các bài kiểm tra đơn vị. Bằng cách kiểm tra mã nguồn, bạn sẽ có thể đảm bảo rằng các bài kiểm tra của bạn bao gồm chung tất cả các đường dẫn mã trong mỗi hành động. Điều này bao gồm việc cung cấp cho các giới hạn của bất kỳ vòng lặp nào và tuân theo tất cả các điều kiện logic. Chắc chắn cũng có thể đạt được mức độ phủ mã này với kiểm tra hộp đen, chẳng hạn như nếu thiết bị đủ đơn giản để thử tất cả các đầu vào hoặc kết hợp trạng thái có thể. Kiểm tra hộp trắng có thể giúp nắm bắt các tình huống khó hiểu mà bạn có thể không nghĩ đến nếu không nhìn thấy mã.

**Trạng thái thử nghiệm** bao gồm việc thực hiện một hành động trên một thiết bị và sau đó kiểm tra xem kết quả mong đợi đã được trả về hay trạng thái của thiết bị đã được cập nhật như mong đợi. Trạng thái thử nghiệm có thể đạt được bất kể bạn đang thử nghiệm hộp trắng hay hộp đen.

**Triển khai thử nghiệm** là một phần mở rộng cho thử nghiệm hộp trắng trong đó bạn kiểm tra xem các phương pháp nhất định có được gọi hay không trong khi thực hiện một hành động. Ở đây, bạn không khẳng định bất kỳ trạng thái nào, nhưng thay vào đó hãy xác minh rằng hành vi nội bộ đang làm những gì được mong đợi.

### Giải phẫu của một bài kiểm tra đơn vị

Một thử nghiệm đơn vị duy nhất xác nhận trạng thái thường được tạo thành từ ba giai đoạn sau:

1. **'Sắp xếp' chuẩn** bị mọi thứ sẵn sàng để thực hiện kiểm tra. Điều này có thể là khai báo các biến, xây dựng các đối tượng được yêu cầu hoặc thiết lập trạng thái của một đơn vị dựa trên các trường hợp chúng ta muốn kiểm tra. Một số hoặc tất cả bước này có thể diễn ra trong phương pháp SetUp của bộ cố định kiểm tra đơn vị hiện tại. Đối với các thử nghiệm đơn vị đơn giản, bước này có thể không cần thiết.
2. **'Hành động'** thực hiện hành động mà chúng tôi đang thử nghiệm trên thiết bị.
3. Kiểm tra **'Khẳng định'** để xem rằng hành động được thực hiện chính xác. Chúng ta muốn kiểm tra xem giá trị trả về của một cuộc gọi phương thức có được mong đợi hay trạng thái của một đối tượng là như mong đợi.

Một ví dụ là:

```
[Test]public void GetMinimum_UnsortedIntegerArray_ReturnsSmallestValue(){  var unsortedArray = new int[] {7,4,9,2,5}; // Arrange   var minimum = Statistics.GetMinimum(unsortedArray); // Act   Assert.AreEqual(2, minimum); // Assert}
```

### Hướng dẫn cấu trúc một bài kiểm tra đơn vị

Dưới đây là một số nguyên tắc bạn có thể làm theo khi viết bài kiểm tra đơn vị:

* Giữ số lượng xác nhận cho mỗi đơn vị thử nghiệm ở mức tối thiểu. Một bài kiểm tra đơn vị là kiểm tra một thứ. Nhiều xác nhận trong một thử nghiệm duy nhất là tốt, nhưng nếu chia các xác nhận thành các thử nghiệm riêng biệt là hợp lý, thì tốt nhất bạn nên làm như vậy.
* Tránh các thử nghiệm ít xác nhận. Đây là những bài kiểm tra không chứa bất kỳ xác nhận nào (hoặc xác minh trong trường hợp kiểm tra triển khai) và được sử dụng để kiểm tra xem một thứ gì đó hoạt động mà không đưa ra ngoại lệ. Tôi thích các bài kiểm tra đơn vị của mình để luôn kiểm tra xem có điều gì đó hoạt động hay không.
* Không lặp lại các xác nhận đã được đề cập trong các thử nghiệm hiện có. Nếu một kiểm thử đơn vị khẳng định rằng một kết quả không phải là rỗng hoặc một tập hợp có chính xác một mục, thì kiểm thử đơn vị tiếp theo không cần lặp lại các khẳng định như vậy trước khi khẳng định trạng thái bổ sung.
* Các xác nhận nên được đặt trong các bài kiểm tra đơn vị phù hợp, thay vì được đưa ra trong các phương pháp trợ giúp. Nếu việc kiểm tra trạng thái hơi phức tạp và phổ biến trong nhiều bài kiểm tra, thì tốt hơn là bạn nên viết một phương thức trợ giúp để kiểm tra trạng thái. Sau đó, thật dễ dàng để đặt các xác nhận bên trong phương thức trợ giúp đó, mặc dù tôi thấy các bài kiểm tra đơn vị dễ đọc hơn nếu chúng chứa các xác nhận có thể kiểm tra boolean được phương thức trợ giúp trả về.
* Mã để sắp xếp, hành động và khẳng định phải nằm trên các dòng riêng của chúng, lý tưởng nhất là có một dòng mới giữa mỗi dòng. Nếu bạn đang xác nhận rằng một phương thức trả về true, bạn có thể thực hiện lệnh gọi phương thức đó ngay bên trong câu lệnh khẳng định. Tôi thấy các bài kiểm tra đơn vị rõ ràng hơn khi chúng được giữ riêng biệt.

### Kiểm tra đôi

Kiểm thử đơn vị chỉ liên quan đến việc kiểm tra một đơn vị duy nhất, chứ không phải là liệu nhiều đơn vị có hoạt động chính xác với nhau hay không. Một số người xem một đơn vị là một lớp, những người khác như một phương thức. Dù bằng cách nào, một đơn vị đơn lẻ thường yêu cầu các đối tượng khác để hoạt động chính xác. Để biên dịch các bài kiểm tra, chúng tôi có thể xây dựng các đối tượng thực và cung cấp chúng cho đơn vị đang kiểm tra theo cách giống như cách chúng tôi làm khi sử dụng đơn vị trong sản xuất. Tuy nhiên, bằng cách làm điều này, chúng tôi bắt đầu rời khỏi việc thực sự chỉ thử nghiệm một đơn vị duy nhất và ghép mã thử nghiệm với nhiều đơn vị hơn những gì nó liên quan. Thậm chí có thể không thực tế khi sử dụng các đối tượng thực nếu chúng kết nối với các công nghệ bên ngoài như dịch vụ web của bên thứ ba, hệ thống xếp hàng hoặc kho dữ liệu.

Đây là nơi các bộ đôi thử nghiệm xuất hiện. Có nhiều bộ đôi thử nghiệm khác nhau mà chúng ta có thể sử dụng để thay thế cho các đối tượng thực tế. Mục đích của việc này chỉ là để đáp ứng các yêu cầu của việc sử dụng thiết bị đang được thử nghiệm. Sau đây là những bộ đôi thử nghiệm mà tôi biết và đã sử dụng.

### Hình nộm

Hình nộm thường là các giá trị không quan trọng nhưng cần thiết để gọi một phương thức mà chúng tôi đang thử nghiệm. Giá trị có thể không liên quan vì nó không ảnh hưởng đến thử nghiệm nhưng cần tồn tại để gọi phương thức. Các tham số của chúng tôi là một ví dụ phổ biến về điều này:

```
[Test]public void GetOccurrences_NewDateTimePattern_HasZeroOccurrences(){  var pattern = new DateTimePattern();  var dummy;   var count = pattern.GetOccurrences(out dummy);   Assert.AreEqual(0, count);}
```

### Stubs

Stub là các phương thức được mã hóa cứng trả về một câu trả lời mong đợi và không quan tâm đến các đối số của phương thức hoặc trạng thái của bất kỳ đối tượng nào, vì vậy không hoạt động bình thường. Chúng có thể là các hàm ẩn danh được chuyển trực tiếp đến một phương thức trên đơn vị mà chúng tôi đang thử nghiệm, trong trường hợp đó, chúng tôi đang kiểm tra xem đơn vị đó có hoạt động chính xác hay không khi hàm trả về kết quả được mã hóa cứng đó. Sơ khai có thể là một phương thức được triển khai như một yêu cầu của một giao diện mà đơn vị được kiểm tra cần gọi. Ví dụ, một sơ khai kiểm tra sự tồn tại của một tệp có thể luôn trả về true nếu chúng tôi không thực sự sử dụng hệ thống tệp trong khi kiểm tra đơn vị:

```
public bool FileExists(string path){  return true;}
```

### Đồ giả

Giả mạo là các đối tượng hoàn toàn có chức năng thường triển khai một giao diện hoặc ít nhất là mở rộng một lớp trừu tượng mà đơn vị được kiểm tra cần. Giả mạo là những triển khai nhanh chóng và bẩn thỉu sẽ không được sử dụng ngoài vai trò là một bài kiểm tra đơn vị kép. Có rất nhiều ví dụ điển hình về hàng giả, gần đây tôi đã sử dụng hàng giả để triển khai giao diện IRedisClient (kho lưu trữ cấu trúc dữ liệu). Thay vì thực sự chạy Redis, dữ liệu được lưu trữ trong cấu trúc dữ liệu C # theo cách rất đơn giản. Các đơn vị đang được kiểm tra yêu cầu IRedisClient để hoạt động có thể được cung cấp một trường hợp giả mạo đó thay vì dựa vào Redis để chạy:

```
public class FakeRedisClient : IRedisClient{  private Dictionary<string, object> _redis = new Dictionary<string,object>();   // and so on   public void AddItemToSet(string setId, string item)  {    object obj;    _redis.TryGetValue(setId, out obj);    HashSet set = (HashSet)obj;    if (set == null)    {      set = new HashSet();      _redis[setId] = set;    }    set.Add(item);  }   // and so forth}
```

### Mocks

Mocks được sử dụng để kiểm tra hành vi hoặc việc thực hiện nội bộ, thay vì trạng thái. Bạn sử dụng chúng để xác minh, ví dụ, một số chức năng nhất định đã được gọi hoặc không được gọi do gọi phương thức bạn đang thử nghiệm. Chế độ giả thường đạt được với sự trợ giúp của các khuôn khổ chế tạo như Moq cho .NET. Bạn mô phỏng một đối tượng từ một giao diện cung cấp cho bạn một đối tượng cụ thể để làm việc. Là một phần của thiết lập, bạn có thể đính kèm các bit logic hoặc các giá trị được mã hóa cứng thay cho các phương thức hoặc thuộc tính. Điều này được thực hiện để đối tượng được chế tạo hoạt động chính xác khi đơn vị được kiểm tra sử dụng. Một đối tượng giả mạo có thể giống về mặt chức năng với đồ giả và có khả năng được sử dụng để kiểm tra trạng thái. Tôi nghiêm túc sử dụng mocks chỉ trong một vài trường hợp tôi viết các bài kiểm tra triển khai.

```
[SetUp]public void SetUp(){  _customer = new Mock();  _customer.Setup(c => c.PaymentID).Returns(1);} [Test]public void CreateSubscription_NewCustomer_ExistingSubscriptionsAreChecked(){  var service = CreatePaymentSubscriptionService();   var subscription = service.CreateSubscription(_customer);   _customer.Verify(c => c.GetSubscriptions());}
```

### Vì vậy, cá nhân tôi đứng ở đâu?

Tôi chủ yếu viết các bài kiểm tra cho mã mà tôi viết và điền vào các bài kiểm tra còn thiếu cho mã mà tôi có quyền truy cập. Do đó, tôi chắc chắn là một người thử nghiệm hộp trắng. Đối với việc triển khai trạng thái và trạng thái, tôi chủ yếu tập trung vào trạng thái thử nghiệm. Tôi muốn đảm bảo rằng một đơn vị đang hoạt động chính xác như được quan sát bên ngoài mà không có giả định nào về cách nó hoàn thành công việc. Đây là cách các đơn vị khác trong một chương trình đang chạy sẽ nhìn thấy và tương tác với nó. Điều này nghe có vẻ giống như kết quả của kiểm tra hộp đen sẽ đạt được. Kiểm tra hộp trắng mang lại cho tôi lợi ích bổ sung là đảm bảo tất cả các đường dẫn mã và bất kỳ trường hợp cạnh nào bị che khuất đều được xác nhận.

Bạn có thể thấy rằng việc thử nghiệm chủ yếu theo tiểu bang khiến việc triển khai tự do thay đổi nhiều hơn. Bất kỳ phần nào của quá trình triển khai không được kết hợp với API chẳng hạn như cấu trúc dữ liệu được sử dụng, cách dữ liệu được định dạng, các khóa được sử dụng để lưu trữ dữ liệu, v.v. thì sẽ không đề cập đến những điều này trong các bài kiểm tra trạng thái của tôi. Bằng cách cập nhật quá trình triển khai nội bộ mà không ảnh hưởng đến API hoặc kết quả mong đợi, bạn sẽ có thể chạy các bài kiểm tra đơn vị để tìm thấy đơn vị vẫn hoạt động như bình thường.

Nếu chủ yếu kiểm tra bằng cách triển khai, các bài kiểm tra đơn vị trở nên kết hợp chặt chẽ với việc triển khai nội bộ đó có thể làm cho các bài kiểm tra khá giòn. Việc thay đổi cách triển khai sẽ phá vỡ các bài kiểm tra và yêu cầu viết lại chúng.

Điều đó nói rằng, một vài thử nghiệm triển khai ở đây và ở đó có thể hữu ích. Ví dụ: có một đơn vị chứa cấu trúc dữ liệu được sử dụng làm bộ đệm ẩn bên trong để giải quyết các vấn đề về hiệu suất. Đây hoàn toàn là một quyết định thực hiện mà bên ngoài đơn vị không cần biết. API của đơn vị không tiết lộ bất kỳ điều gì liên quan đến trạng thái của bộ nhớ cache nội bộ này, vì vậy không có cách nào để khẳng định điều đó. Để yên tâm, bạn có thể kiểm tra xem bộ nhớ đệm này có được quản lý tốt hay không, chẳng hạn như xóa các mục khi thích hợp. Để làm điều này, có thể tốt hơn là sử dụng thử nghiệm triển khai, nếu bạn không muốn phơi bày sự tồn tại của bộ nhớ đệm theo một cách nào đó để kiểm tra trạng thái của nó.

Tôi không nghiêm khắc buộc bản thân chỉ kiểm tra theo trạng thái và bỏ qua kiểm tra triển khai. Tôi nghĩ rằng thật tốt khi biết những cách tiếp cận nào có sẵn và chọn công cụ phù hợp cho công việc.

Bạn có bất kỳ ví dụ kiểm tra đơn vị hoặc nguyên tắc cá nhân nào mà bạn muốn chia sẻ không? Cho chúng tôi biết trong các ý kiến ​​dưới đây.
