簡體   English   中英

使用帶有酶的 rxjs 庫測試反應組件

[英]Testing react component using rxjs library with enzyme

我在我的反應項目中使用rxjs ,並且各種組件都訂閱了數據流。 有沒有辦法測試訂閱主題的此類組件:

例如

import { Subject } from 'rxjs';
import { useState, useEffect } from 'react';
import styles from '../index.scss';

function useObservable({ subject, initialValue }) {
  const [value, setValue] = useState(initialValue);

  useEffect(() => {
    const subscription = subject.subscribe({
      next: (v) => setValue(v),
    });
    return () => subscription.unsubscribe();
  }, [subject]);
  return value;
}

const subject = new Subject();
function getRndInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function OrderProgress({ orderData }) {
  setInterval(() => {
    subject.next({
      change: getRndInteger(0, 120),
    });
  }, 10000);

  const LIVE_VALUE = useObservable({
    subject,
    initialValue: { change: 100 },
  });
  const LIVE_STATUS = LIVE_VALUE.change;

  return (
    <div className={styles.progressCard}>
      <div
        className={styles.progressBar}
        style={{ width: `${((100 / (orderData.price)) * LIVE_STATUS)}%`, maxWidth: '99%' }}
      />
      <div className={styles.liveData}>
        <span className={styles.label}>LIVE</span>
        {/* TODO: data from socket */}
        <span className={styles.value}>{LIVE_STATUS}</span>
      </div>
      <div className={styles.progressDetail}>
        {orderData.exchange}
        : Awaiting Execution…
      </div>
      <div className={styles.targetData}>
        <span className={styles.label}>Target</span>
        <span className={styles.value}>{orderData.price}</span>
      </div>
    </div>
  );
}

我怎樣才能正確地測試上述組件,最好使用玩笑和酶。

這是單元測試解決方案:

index.tsx

import { Subject } from "rxjs";
import React, { useState, useEffect } from "react";
// import styles from "../index.scss";

const styles = {
  progressCard: "progressCard",
  progressBar: "progressBar",
  liveData: "liveData",
  label: "label",
  value: "value",
  progressDetail: "progressDetail",
  targetData: "targetData"
};

function useObservable({ subject, initialValue }) {
  const [value, setValue] = useState(initialValue);

  useEffect(() => {
    const subscription = subject.subscribe({
      next: v => setValue(v)
    });
    return () => subscription.unsubscribe();
  }, [subject]);
  return value;
}

const subject = new Subject();

function getRndInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function OrderProgress({ orderData }) {
  setInterval(() => {
    subject.next({
      change: exports.getRndInteger(0, 120)
    });
  }, 10000);

  const LIVE_VALUE = useObservable({
    subject,
    initialValue: { change: 100 }
  });
  const LIVE_STATUS = LIVE_VALUE.change;
  return (
    <div className={styles.progressCard}>
      <div
        className={styles.progressBar}
        style={{
          width: `${(100 / orderData.price) * LIVE_STATUS}%`,
          maxWidth: "99%"
        }}
      />
      <div className={styles.liveData}>
        <span className={styles.label}>LIVE</span>
        {/* TODO: data from socket */}
        <span className={styles.value}>{LIVE_STATUS}</span>
      </div>
      <div className={styles.progressDetail}>
        {orderData.exchange}: Awaiting Execution…
      </div>
      <div className={styles.targetData}>
        <span className={styles.label}>Target</span>
        <span className={styles.value}>{orderData.price}</span>
      </div>
    </div>
  );
}

exports.getRndInteger = getRndInteger;
exports.OrderProgress = OrderProgress;
exports.useObservable = useObservable;
exports.subject = subject;

index.spec.tsx

const { OrderProgress } = require("./");
const mod = require("./");
import React from "react";
import { mount } from "enzyme";
import { act } from "react-dom/test-utils";

jest.useFakeTimers();

afterEach(() => {
  jest.restoreAllMocks();
});

describe("OrderProgress", () => {
  it("should passs", () => {
    const getRndIntegerSpy = jest
      .spyOn(mod, "getRndInteger")
      .mockReturnValueOnce(200);
    const subscribeSpy = jest.spyOn(mod.subject, "subscribe");

    const mProps = { orderData: { price: 1000 } };
    const wrapper = mount(<OrderProgress {...mProps}></OrderProgress>);
    expect(wrapper.find(".progressBar").prop("style")).toEqual({
      width: "10%",
      maxWidth: "99%"
    });
    expect(wrapper).toMatchSnapshot();
    act(() => {
      jest.advanceTimersByTime(10 * 1000);
    });
    wrapper.update();
    expect(wrapper.find(".progressBar").prop("style")).toEqual({
      width: "20%",
      maxWidth: "99%"
    });

    expect(subscribeSpy).toBeCalledTimes(1);
    expect(getRndIntegerSpy).toBeCalledWith(0, 120);
    expect(wrapper).toMatchSnapshot();
  });
});

describe("getRndInteger", () => {
  it("should pass", () => {
    const randomSpy = jest.spyOn(Math, "random").mockReturnValueOnce(1);
    const actual = mod.getRndInteger(1, 100);
    expect(actual).toBe(101);
    expect(randomSpy).toBeCalledTimes(1);
  });
});

帶有覆蓋率報告的單元測試結果:

PASS  src/stackoverflow/59028966/index.spec.tsx (8.918s)
  OrderProgress
    ✓ should passs (64ms)
  getRndInteger
    ✓ should pass (1ms)

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |    96.15 |      100 |    85.71 |      100 |                   |
 index.tsx |    96.15 |      100 |    85.71 |      100 |                   |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        10.504s

index.spec.tsx.snap

// Jest Snapshot v1

exports[`OrderProgress should passs 1`] = `
<OrderProgress
  orderData={
    Object {
      "price": 1000,
    }
  }
>
  <div
    className="progressCard"
  >
    <div
      className="progressBar"
      style={
        Object {
          "maxWidth": "99%",
          "width": "10%",
        }
      }
    />
    <div
      className="liveData"
    >
      <span
        className="label"
      >
        LIVE
      </span>
      <span
        className="value"
      >
        100
      </span>
    </div>
    <div
      className="progressDetail"
    >
      : Awaiting Execution…
    </div>
    <div
      className="targetData"
    >
      <span
        className="label"
      >
        Target
      </span>
      <span
        className="value"
      >
        1000
      </span>
    </div>
  </div>
</OrderProgress>
`;

exports[`OrderProgress should passs 2`] = `
<OrderProgress
  orderData={
    Object {
      "price": 1000,
    }
  }
>
  <div
    className="progressCard"
  >
    <div
      className="progressBar"
      style={
        Object {
          "maxWidth": "99%",
          "width": "20%",
        }
      }
    />
    <div
      className="liveData"
    >
      <span
        className="label"
      >
        LIVE
      </span>
      <span
        className="value"
      >
        200
      </span>
    </div>
    <div
      className="progressDetail"
    >
      : Awaiting Execution…
    </div>
    <div
      className="targetData"
    >
      <span
        className="label"
      >
        Target
      </span>
      <span
        className="value"
      >
        1000
      </span>
    </div>
  </div>
</OrderProgress>
`;

源代碼: https : //github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59028966

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM