Написание кода при разработке через тестирование

Разработка через тестирование, также известная как TDD, - это подход к разработке программного обеспечения, при котором вы должны написать тест перед написанием любого функционального кода.

Обзор

Здесь у нас есть GET API:

url = "https://jsonplaceholder.typicode.com/photos"

Что возвращает массив объектов, как показано ниже:

[
  {
    "albumId": 1,
    "id": 1,
    "title": "accusamus beatae ad facilis cum similique qui sunt",
    "url": "https://via.placeholder.com/600/92c952",
    "thumbnailUrl": "https://via.placeholder.com/150/92c952"
  },
  {
    "albumId": 1,
    "id": 2,
    "title": "reprehenderit est deserunt velit ipsam",
    "url": "https://via.placeholder.com/600/771796",
    "thumbnailUrl": "https://via.placeholder.com/150/771796"
  }
]

Мы создадим класс диспетчера API, который внутренне использует Axios для реализации REST.

Нам нужно создать класс с именем ApiManager.js и соответствующий тестовый файл с именем ApiManager.test.js.

Написание базового теста и объекта класса

Давайте начнем с написания теста. Давайте предположим, что нашему классу ApiManager необходимо создать экземпляр с некоторым URL-адресом, а out apiManagerInstance имеет свойство с именем url, которое необходимо создать с помощью конструктора.

import ApiManager from "./ApiManager";
describe('ApiManager Construction test', () => {
 
 const mockURL = "https://mockurl.com"; //
 let instance = new ApiManager(mockURL);
 
 it("instance url property set properly",()=>{
    expect(instance.url).toEqual(mockURL)
 })
})

Очевидно, что проверка завершится неудачно, потому что в ApiManager.js ничего нет.

Давайте напишем код для прохождения теста в ApiManager.js.

export default class ApiManager{
 
 constructor(url){
     this.url = url
 }
}

Теперь наш тест пройден, и пришло время реорганизовать реальную реализацию. Вроде все нормально. Так что рефакторингу нечего.

Реализация метода GET с использованием разработки через тестирование

Теперь у нашего ApiManager будет метод getPhotos, который вернет из объекта массив фотографий с необходимыми данными. В нашем случае нам понадобится всего два поля из объекта {идентификатор альбома, url}

Сценарий будет как

  • В ApiManager будет метод под названием getPhotos.
  • getPhotos вызовет метод get axios с помощью instance.url.
  • getPhotos вернет массив, содержащий {идентификатор альбома, url} из возвращенного массива объекта axios.get.
  • getPhotos выдаст ошибку, если axios.get вернет ошибку

Настраивать

describe('getPhotos method Test', () => {
    
  const mockURL = "https://mockurl.com";
  let instance = new ApiManager(mockURL);
     // Our Test cases will Goes Here !!!!
}

Сценарий 1: у ApiManager будет метод getPhotos

it(“getPhotos method defined”,()=>{
  expect(instance.getPhotos).toBeDefined();
})

Чтобы передать этот случай, напишите метод с именем getPhotos

getPhotos(){
}

Сейчас проходят испытания. 👌

Сценарий 2: getPhotos вызовет метод axios get с instance.url

it(“getPhotos will call axios.get method with instance.utl”,()=>{
  
  jest.spyOn(Axios,”get”) // Axios get need to be spied by Jest 
  
  instance.getPhotos(); //
  expect(Axios.get).toHaveBeenCalledWith(instance.url);
})

Не получается правильно. Не беспокойтесь. Давай пройдем их

getPhotos(){
  Axios.get(this.url)
}

Теперь его снова прохождение ✔️.

Сценарий 3: getPhotos вернет массив из {id альбома, url}

it("getPhotos will return proper array of object for axios mock response",async ()=>{

const mockPhotos = [{ 
  "albumId": 1,
  "id": 1,
  "title": "accusamus beatae ad facilis cum similique qui sunt",
  "url": "https://via.placeholder.com/600/92c952",
  "thumbnailUrl": "https://via.placeholder.com/150/92c952"},
 { 
  "albumId": 1,
  "id": 2,
  "title": "reprehenderit est deserunt velit ipsam",
  "url": "https://via.placeholder.com/600/771796",
  "thumbnailUrl": "https://via.placeholder.com/150/771796"
}]
const expectedResponse =[ {
 "albumId": 1,
 "url": "https://via.placeholder.com/600/92c952",
 },
{
 "albumId": 1,
 "url": "https://via.placeholder.com/600/771796",
}]
 jest.spyOn(Axios,"get").mockResolvedValueOnce(mockPhotos);
 const response = await instance.getPhotos();
 expect(response).toEqual(expectedResponse);
})

Давайте передадим это

getPhotos(){
 return  new Promise((resolve,reject)=>{
    Axios.get(this.url).then(response=>{
       const filterArr = response.map(item=> {
           return {
                "albumId": item.albumId,
                "url": item.url
                }
        })
       resolve(filterArr)
 }).catch(error=>{
       
 })
});
}

Потрясающе, теперь все хорошо. Правильно? 🤘

Сценарий 4: getPhotos выдаст ошибку, если axios.get вернет ошибку

it("getPhotos will throw error if axios.get return error",async ()=>{
 const mockError = {
    error:{
       msg:"Something Wrong"
    }
 }
 jest.spyOn(Axios,"get").mockRejectedValueOnce(mockError);
 await instance.getPhotos().catch(error=>{
 expect(error.error.msg).toEqual("Something Wrong")
 });
})

Давайте передадим это, добавив только одну строку, то есть reject (error) внутри блока catch.

getPhotos(){
 return  new Promise((resolve,reject)=>{
    Axios.get(this.url).then(response=>{
       const filterArr = response.map(item=> {
           return {
                "albumId": item.albumId,
                "url": item.url
                }
        })
       resolve(filterArr)
 }).catch(error=>{
       reject(error) // only add this
})
});
}

Теперь все прошло правильно? ✔️

Престижность. У нас есть полная реализация HTTP GET с помощью разработки через тестирование. 👍🏻

Примечание. Не забудьте выполнить рефакторинг после прохождения тестов, если это необходимо.

Спасибо за прочтение! 🍻

JavaScript на простом английском языке

Понравилась эта статья? Если да, то получите больше похожего контента, подписавшись на наш канал YouTube!