농담으로 비웃다
                                            
                                                
                                                
                                                
                                                
                                                
                                                 25247 단어  reactbeginnersjavascripttesting
                    
실제 코드를 시작하기 전에 본고는react,jest,enzyme의 기본 지식을 이해하시기 바랍니다.나는 그것의 일부 기본적인 부분을 설명할 것이지만, 전체 기지는 아니다. 왜냐하면 이것은 상당히 큰 범위이기 때문이다.나는 https://codesandbox.io/에서 시연을 만들기 시작했지만jest와 관련된 문제에 부딪혔다.mock이 지원되지 않았습니다.그래서 이 프레젠테이션을 로컬에서 만들어서github에 놓았습니다. https://github.com/JHotterbeekx/jest-mock-with-callback-demo 에서 찾을 수 있습니다.
답전
우선 리콜이 무엇인지 간단명료하게 설명해 봅시다.나는 네가 이 방법들을 잘 알고 있다고 가정한다면, 우리는 이 기본적인 방법을 예로 들자.
function doSomething(argument) {
  console.log(argument);
}
doSomething("I'm awesome")
function doSomething(argument) {
  console.log(argument);
}
function doAnotherThing() {
  console.log("I'm awesome");
}
doSomething(doAnotherThing);
function doAnotherThing() {
  console.log("I'm awesome");
}
function doSomething(argument) {
  argument();
}
function doAnotherThing() {
  console.log("I'm awesome");
}
doSomething(doAnotherThing);
I'm awesome
function doSomething(argument, whatYouAre) {
  argument(whatYouAre);
}
function doAnotherThing(whatIAm) {
  console.log("I'm " + whatIAm);
}
doSomething(doAnotherThing, "even more awesome");
I'm even more awesome
실제 용례
좋아, 만약 우리가 응용 프로그램이 하나 있다면, 그것은 두 개의 구성 요소가 있을 것이다.먼저 DataDisplayer가 DataRetriever에서 검색한 결과를 보여줍니다.그러나 이 검색기는 비동기적으로 작동하기 때문에 결과만 돌려보낼 수 없다.이 점을 해낼 수 있는 몇 가지 방법이 있지만, 본 예에서 우리는 리셋 방법을 사용할 것이다.코드에 주석을 추가해서 DataDisplayer를 설명하려고 했습니다.
import React from "react";
import DataRetriever from "./DataRetriever";
export default class DataDisplayer extends React.Component {
  constructor(props) {
    super(props);
    // We initialize the state with a property that contains a boolean telling us if data is
    // available, which will be set to 'true' once the callback method is called. And a data
    // property which will be filled on callback containing a string with a title.
    this.state = {
      dataAvailable: false,
      data: null
    };
  }
  // We use the componentDidMount to trigger the retrieval of the data once the component is
  // mounted. Which means the component first mounts with its default state and than triggers
  // this method so data is retrieved.
  componentDidMount() {
    // We create a new instance of data retriever and call the retrieve method. In this
    // retrieve method we pass a so-called callback method as a parameter. This method will
    // be called inside the retrieve method. As you can see the method expects a title parameter
    // which it will set on the data property in the state and also setting the dataAvailable
    // property to true;
    new DataRetriever().Retrieve(title => {
      this.setState({
        dataAvailable: true,
        data: title
      });
    });
  }
  // This render method will initially render the text 'Data not available', because in the 
  // initial state the property dataAvailable is false. Once data is retrieved and the callback
  // method has been called the state will update, which triggers a re-render, so the render
  // is executed again. Now the dataAvailable will be true and the content in data will be shown.
  render() {
    if (!this.state.dataAvailable) return <div>Data not available</div>;
    return (
      <div>
        Data value: <strong>{this.state.data}</strong>
      </div>
    );
  }
}
이제 DataRetriever를 살펴보겠습니다. 리셋 방법은 바로 이곳에서 전달됩니다.
export default class DataRetriever {
  // This demo method calls an open API, then translates the response to JSON. Once that is done
  // it calls the passed in callbackMethod with the title property as parameter. So when the API
  // gives us { title: 'myTitle' }, the code will perform callbackMethod('myTitle')
  Retrieve(callbackMethod) {
    fetch("https://jsonplaceholder.typicode.com/todos/1")
      .then(response => {
        return response.json();
      })
      .then(responseJson => callbackMethod(responseJson.title));
  }
}
리셋 시뮬레이션을 사용하여 테스트를 진행하다
우리는 왜 또 비웃어야 합니까?우리는 단원 테스트를 작성하고 있습니다. 단원 테스트의 문제는 그들이 단원을 테스트하기를 바라는 것입니다.이 경우 어셈블리는 하나뿐입니다.DataDisplayer만 호출하면 DataRetriever를 사용하지만 이 구성 요소는 직접 테스트를 수행했을 수도 있습니다.실제로 DataRetriever가 무엇을 할지 예측하고 다른 구성 요소에서 제어하기를 원합니다.이 격리가 필요한 또 다른 이유는 DataRetriever를 중단할 때, 이 구성 요소의 테스트 중단이 어떤 방식으로 모든 구성 요소를 사용할 수 있기만을 원하기 때문입니다.DataRetriever의 논리를 바꿀 때 수십 개의 테스트를 바꿔야 한다고 상상해 보세요. 이것은 당신이 원하지 않는 것입니다.
나는 네가 다른 구성 요소를 예측하고 싶다고 언급했다. 이 예에서 DataRetriever는 무엇을 하는가.우리는 조롱을 통해 이 점을 해냈다.Mocking은 DataRetriever 구성 요소를 가짜 (또는 아날로그) 구성 요소로 교체할 수 있도록 합니다. 이 구성 요소는 우리의 요구를 충족시킵니다.우선 테스트 파일에서 기본 프레임워크를 구축합시다.
import React from "react";
import { mount } from "enzyme";
import DataDisplayer from "./DataDisplayer";
// We want to test DataDisplayer in an isolated state, but DataDisplayer uses DataRetriever.
// To keep the isolation we will need to mock out the DataRetriever. This way we control 
// what this component does and we can predict the outcome. To do this we need to do a manual
// mock, we can do this by importing the component we want to mock, and then defining a mock
// om that import.
import DataRetriever from "./DataRetriever";
jest.mock("./DataRetriever");
describe("DataDisplayer", () => {
  // Before each test we want to reset the state of the mocked component, so each test can
  // mock the component in the way it needs to be mocked. Should you have any default mock
  // needed that is required for every test, this is the place to do this.
  beforeEach(() => {
    DataRetriever.mockClear();
  });
});
// In this test we will mock the DataRetriever in a way that it will call the callback method
// we pass to it, and call it with "fakeTitle" as argument. This simulates that the API has
// given us a result with { title: "fakeTitle" } in it.
it("Should show the data, When retrieved", () => {
  // We are going to set up a mock implementation on the DataRetriever, we tell it when the code
  // uses DataRetiever instead of the original code it will receive a mocked object. This mocked
  // object has one method call "Retrieve".
  DataRetriever.mockImplementation(() => {
    return {
      // The retrieve method is defined as a method with is own logic. It's a method that gets 
      // another method as argument, the so-called callback method. And the only thing it does
      // is call this method with the argument "fakeTitle". This means that when the code will
      // create a new instance of DataRetriever and calls Retrieve(callback) that the method
      // callback is instantly called with the argument "fakeTitle". Simulating the API returning
      // this result.
      Retrieve: (callback) => callback("fakeTitle")
    }
  });
  // We mount the compont through enzyme. This renders the component with a fake DOM making us
  // able to see the result that would be rendered. Usually in unit tests I'd prefer the shallow
  // mount which doesn't execute lifecycle methods, but in this case part of the logic of our
  // component is in the componentDidMount lifecycle method, so we need mount to make sure this
  // lifecycle is triggerd.
  var wrapper = mount(<DataDisplayer />);
  // Since we fake a result coming back from the retriever, we expect the text to actually show
  // the word "fakeTitle" in the component.
  expect(wrapper.text()).toContain("fakeTitle");
});
이제 우리는 또 다른 장면을 테스트해야 하는데 만약 API가 실패하면 어떻게 합니까?혹은 어떤 이유로든 리콜이 아직 호출되지 않았다.이 때문에 테스트를 작성합시다.
// In this test we will mock the DataRetriever in a way that it will not call the callback
// method we pass to it. This simulates tha API not being finished or returning an error.
it("Should show not available, When data has not been retrieved", () => {
  // We are setting up a new mock implementation on the DataRetriever again.
  DataRetriever.mockImplementation(() => {
    return {
      // This is where we made it a little different. Instead of passing a method which does
      // an instant call to the callback we pass an empty method that doesn't do anything. So
      // when the code will create a new instance of DataRetriever and calls Retrieve(callback)
      // nothing is done with this callback. To make it more clear you could also read this line
      // as: Retriever: (callback) => { /* Do Nothing */ }
      Retrieve: () => {}
    }
  });
  //We mount the component again, since we need to use the lifecycle methods.
  var wrapper = mount(<DataDisplayer />);
  // Since we fake no result coming back from the retriever we don't expect any title appearing
  // on the page, but instead we expect to see the text "not available"
  expect(wrapper.text()).toContain("not available");
});
리소스
인터넷에는 자원이 가득 차 있다. 비록 이 문제에 있어서는 유행이 지난 자원을 사용하지 않도록 주의해야 한다.제스트와 모킹에 있어서 좋은 출발점은 그들이 https://jestjs.io/docs/en/getting-started에 관한 문서, 특히 모킹 ES6류https://jestjs.io/docs/en/es6-class-mocks에 관한 부분이다.그들의 문서는 사람들로 하여금 어찌할 바를 모르게 할 수도 있다. 특히 많은 방법으로 사물을 모의할 수 있지만, 이것은 매우 좋은 문서이기 때문에 한번 시도해 보세요.이외에도 구글로 원하는 것을 검색하면 이런 방식으로 답을 찾을 수 있다.너는 충분한 문장과 산더미처럼 쌓인 문제에 부딪혀 문제를 해결할 수 있을 것이다. 문장의 날짜를 보고 갱신을 시도하기만 하면 된다.
약속 및 비동기식/대기
비록 이 예는 리셋 방법을 사용했지만, 현재 이러한 방법은 대부분promises나 업데이트된 async/await 구조에 의해 대체되었다.이것은 결코 네가 다시 리셋을 사용할 수 없다는 것을 의미하지 않는다.코드에 의미 있는 것만 사용하면
마무리
이때 너의 머리가 회전하고 있을지도 모르니, 그것이 멈출까 봐 걱정하지 마라.아니면 생각할지도 몰라, 이게 다야?그럼 내가 말해줄게, 너한테 좋을 거야!소프트웨어 개발의 대다수 개념과 마찬가지로, 너는 반드시 어딘가에서 그것을 보고 나서 스스로 해야 한다.그리고 한 번 또 한 번 한다.마지막으로, 그들로 하여금 견지하게 하고, 개선하게 하며, 그들을 다른 사람에게 가르치게 하다.그래서 나는 누군가가 어떻게 그나 그녀의 방식으로 이 점을 해낼 수 있는지에 관한 글을 쓰기를 기대하고 있다.너는 독서에서 배우고, 실천에서 배우며, 공유를 통해 그것을 견지해 나가라.
Reference
이 문제에 관하여(농담으로 비웃다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/jhotterbeekx/mocking-with-callbacks-in-jest-2423텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)