如何使用Jest测试一个带有localStorage的react组件?

qmelpv7a  于 2023-03-16  发布在  Jest
关注(0)|答案(2)|浏览(168)

我有一个调用本地存储的组件,我想用jestJS测试它。据我所知,jest不支持对localStorage的调用。
这是我需要测试的组件:

const NavBar: React.FC = () => {
  const history = useHistory();

  const handleCLick = () => {
    localStorage.clear();
    history.push('/login');
  };

  return (
    <div>
      <header>
        <div className="banner">
          <div className="container">
            <img
              className="icon "
              alt="icon"
              title="icon"
              src={favicon57}
            />
            <p>Official website of the Stuff</p>
          </div>
        </div>
        <nav className="navbar navbar-expand-md navbar-dark fixed-top">
          <div className="container">
            <div className="navbar-header">
              <img
                className="logo "
                alt="logo"
                title="Logo"
                src={Blah}
              />
            </div>
            <button
              className="navbar-toggler"
              type="button"
              data-toggle="collapse"
              data-target="#navbarCollapse"
              aria-controls="navbarCollapse"
              aria-expanded="false"
              aria-label="Toggle navigation"
            >
              <span className="navbar-toggler-icon" />
            </button>
            <div className="collapse navbar-collapse" id="navbarCollapse">
              <ul className="navbar-nav ml-auto">
                {isTokenAdmin() ? (
                  <li className="nav-item">
                    <a id="nav-users" className="nav-link" href={ADMIN_URL}>
                      View Users
                    </a>
                  </li>
                ) : (
                  <div> </div>
                )}
                {isTokenActive() ? (
                  <li className="nav-item">
                    <a id="nav-log-out" className="nav-link" href={APP_URL}>
                      Locations
                    </a>
                  </li>
                ) : (
                  <div> </div>
                )}
                {isTokenActive() ? (
                  <li className="nav-item">
                    <a
                      id="nav-log-out"
                      className="nav-link"
                      href={LOGIN_URL}
                      onClick={() => {
                        handleCLick();
                      }}
                    >
                      Logout
                    </a>
                  </li>
                ) : (
                  <div> </div>
                )}
              </ul>
            </div>
          </div>
        </nav>
      </header>
    </div>
  );
};

export default NavBar;

正如你所看到的,我正在根据我存储在localStorage中的令牌来呈现按钮,你如何让它达到100%的测试覆盖率呢?

编辑:

用于获取令牌的函数代码如下:

export const isTokenActive = (): boolean => {
  const userToken: string | null = localStorage.getItem('exp');
  if (typeof userToken === 'string') {
    return new Date().getTime() < Number.parseInt(userToken, 10);
  }
  return false;
};

export const isTokenAdmin = (): boolean => {
  const userToken: string | null = localStorage.getItem('access_token');
  if (typeof userToken === 'string') {
    const decodedToken: TokenDetails = jwt_decode(userToken);
    return decodedToken.authorities[0] === 'ROLE_Administrator';
  }
  return false;
};
arknldoa

arknldoa1#

你说的对,Jest不支持对localStorage的调用,它不是浏览器,也不实现localStorage。
解决方案是模仿您自己的伪localStorage支持,如下所示:
browserMocks.js

const localStorageMock = (function() {
  let store = {}

  return {
    getItem: function(key) {
      return store[key] || null
    },
    setItem: function(key, value) {
      store[key] = value.toString()
    },
    removeItem: function(key) {
      delete store[key]
    },
    clear: function() {
      store = {}
    }
  }
})()

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock
})

Jest配置(可以在您的package.json中)

"jest": {
    "setupFiles": [
      "<rootDir>/__jest__/browserMocks.js",

然后,要查看是否调用了本地存储,您可以像这样监视它:

describe('signOutUser', () => {
  it('should sign out a user', async () => {
    const spyLoStoRemove = jest.spyOn(localStorage, 'removeItem')

    await signOutUser()
    
    expect(spyLoStoRemove).toHaveBeenCalled()
    expect(spyLoStoRemove).toHaveBeenCalledTimes(2)
  })
})
eivnm1vs

eivnm1vs2#

嘲笑@Jonathan欧文在大多数情况下确实有效,但并不总是如此,因为它并没有真正模仿本地存储的结构。
它不会工作,例如当你使用in运算符,例如。

if (!('MY_KEY' in localStorage)) {
  return 'something'
}

它不会工作,因为原始本地存储示例在自身上存储密钥,方法来自其存储原型,这种行为模拟经典继承,但实际上更多的是委托而不是继承,我们可以在例如Chrome控制台中验证:x1c 0d1x原型部件:

在@欧文模拟中,我们没有将值-键对存储在本地存储示例中,而是存储在单独的内部闭包对象中,因此我们不能以与原始本地存储类似的方式使用它
改进版本:

const storagePrototype = {
  getItem: function (key) {
    return localStorageMock[key] || null;
  },
  setItem: function (key, value) {
    localStorageMock[key] = value.toString();
  },
  removeItem: function (key) {
    delete localStorageMock[key];
  },
  clear: function () {
    Object.keys(fakeLocalStorage).forEach(
      (key) => delete localStorageMock[key]
    );
  },
};

export const localStorageMock = Object.create(storagePrototype);

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock
})

基于类的版本:

class Storage {
  getItem(key) {
    return this[key] || null;
  }
  setItem(key, value) {
    this[key] = value.toString();
  }
  removeItem(key) {
    delete this[key];
  }
  clear() {
    Object.keys(this).forEach((key) => delete this[key]);
  }
}

export const localStorageMock = Object.create(new Storage());

相关问题