お知らせ
現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。
基本出来ないのでwindow.location.href
を操作するためのラッパーを作って、それをjest.spyOn()
して解決する
理由はjsdomが対応していないため
global.navigator
をモックにする方法
jest
のモック機能はプロパティのモックが出来ないので、実オブジェクトを強制的に書き換えて実施するObject.defineProperty()
を利用して実装
value
, プロパティが返す値configurable
, 再定義可能かどうか、設定しないと再実行でコケるafterAll()
でnavigator.userAgent
を初期値に戻すのが望ましいObject.defineProperty(global.navigator, 'userAgent', {
value:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36',
configurable: true,
});
axios
そのものをモックして、その結果に応じた処理が想定通り走っているかどうかを確認したいcreate-react-app
で作成したTypeScript Reactのプロジェクトにnpm t
を掛けてテストすることを想定Env | Ver |
---|---|
react | 17.0.1 |
react-scripts | 4.0.2 |
typescript | 4.1.3 |
import axios from 'axios';
import { useEffect } from 'react';
export const AsyncHookExample = () => {
useEffect(() => {
axios
.get('https://localhost/')
.then((res) => console.log(res))
.catch((err) => console.error(err));
}, []);
return <p />;
};
axios
をモックした上で、モック関数のPromise
の解決を待ち、 Promise
のコールバックをspyOn
して検査する内容Promise
の解決法を求めるのにハマりましたが、これはfoo.mock.results[0].value
に対してawait expect(foo.mock.results[0].value).resolves
としてやればいけるimport { render } from '@testing-library/react';
import axios from 'axios';
import { AsyncHookExample } from './AsyncHookExample';
// `axios` をモックにする
jest.mock('axios');
// `axios` のモックを取得
const mockedAxios = axios as jest.Mocked<typeof axios>;
// `console.log()` を `spyOn'
const spiedConsoleLog = jest.spyOn(console, 'log');
// `console.error()` を `spyOn'
const spiedConsoleError = jest.spyOn(console, 'error');
describe('AsyncHookExample', () => {
it('resolve promise in Hook', async () => {
// テストに期待する結果
// `axios` はモックなので値は適当
const beResult = {
status: 200,
data: null,
};
// `axios` のモックが `resolve` する値を設定
mockedAxios.get.mockResolvedValue(beResult);
// コンポーネントを `render` して `useEffect` を走らせる
render(<AsyncHookExample />);
// `axios.get()` が呼ばれたことを確認
expect(mockedAxios.get).toBeCalled();
// モックの結果を取得
const testResult = mockedAxios.get.mock.results[0].value;
// `reject` された値が期待通りであることを確認
await expect(testResult).resolves.toEqual(beResult);
// `useEffect` の中の `Promise` の中にある `console.log()` が呼ばれたことを確認
expect(spiedConsoleLog).toBeCalled();
});
it('reject promise in Hook', async () => {
// テストに期待する結果
// `axios` はモックなので値は適当
const beResult = {
status: 400,
data: null,
};
// `axios` のモックが `reject` する値を設定
mockedAxios.get.mockRejectedValue(beResult);
// コンポーネントを `render` して `useEffect` を走らせる
render(<AsyncHookExample />);
// `axios.get()` が呼ばれたことを確認
expect(mockedAxios.get).toBeCalled();
// モックの結果を取得
const testResult = mockedAxios.get.mock.results[0].value;
// `reject` された値が期待通りであることを確認
await expect(testResult).rejects.toEqual(beResult);
// `useEffect` の中の `Promise` の中にある `console.error()` が呼ばれたことを確認
expect(spiedConsoleError).toBeCalled();
});
});
mockResolvedValue
のスコープやらモックのPromise
を解決させるマッチャやら調べるのに手間取った
Promise
が解決した結果がどこに入るのかとか、兎に角あれこれexpect(mockedAxios.get).toBeCalled()
が通るのはすぐに気づいたので、なら出来るだろうとひたすら調べてた
nextjs.orgをサラーっと読み流したログ
Next.js 9.3時点でのドキュメント、SSRについては基本言及しない
ドキュメントの和訳でもなんでもなく単なる書き殴りです
書き殴りなので情報の漏れなどは当然ありますし、そもそも知りたいところしか読んでません。
つまり、ここに書いてあることは何も信じてはならないので、真実は君の目で確かめて欲しい。
私はまだコードも書いてないし何もわからない、知らない、関知できない!すまないッッ!!!!
create-next-app --example with-typescript project-name
dev
- 開発サーバーが上がるよbuild
- プロダクションビルドを生成するよstart
- プロダクションサーバーを上げるよpages/about.tsx
のようにすると静的ページが生えるよ。やったね。pages/posts/[id].tsx
なら動的ルーティングも可能だ!
export
するときは面倒でもexport default
しないと上手く行かないので注意しよう
export const Foo = () => { return <p /> };
Next.jsでは次の二種類のレンダリングに対応しているよ
勿論、CSRはどちらでも使えるのだけど、CSRのデータフェッチについてはこっち
データの取得方法について
getStaticProps
export const getStaticProps = (context) => {
return {
props: {}, // will be passed to the page component as props
};
};
context
の中身はここを参照
props
required。突っ込みたいデータを入れてねrevalidate
Optional。ISRをする時に使うよ。詳しくはここ。notFound
Optional。true
にすると404が表示される。使用例は以下のような感じexport const getStaticProps = (context) => {
const res = await fetch(`https://.../data`);
const data = await res.json();
if (!data) {
return {
notFound: true,
};
}
return {
props: {}, // will be passed to the page component as props
};
};
public/
配下は静的ファイルの配置場所として使える
fs.readFileSync('public/biography.yaml')
getStaticProps()
にpath.join(process.cwd(), 'contents/biography.yaml')
みたいに書いてやるとpublic外のファイルを読めますhistory
のバージョンとreact-router-dom
のバージョンが上手く噛み合ってないと既存実装で型エラーが出る可能性があるEnv | Ver |
---|---|
react | 17.0.2 |
react-dom | 17.0.2 |
react-router-dom | 5.2.0 |
react-scripts | 4.0.3 |
typescript | 4.2.3 |
history | 4.10.1 |
ToPage
に飛ばしたlocation.state
を検査する内容ToPage.tsx
export const ToPage = () => {
const location = useLocation<{ test?: string }>();
return <p>from {location.state?.test ?? 'nothing'}</p>;
};
ToPage.spec.tsx
const renderWithRouter = (ui: JSX.Element, { route = '/' } = {}) => {
window.history.pushState({}, 'Test page', route);
return render(ui, { wrapper: BrowserRouter });
};
describe('onload', () => {
it('from history.push', () => {
const history = createMemoryHistory<{ test: string }>();
history.push(AppRoute.to.path, { test: 'foo' });
const { container } = renderWithRouter(
<Router history={history}>
<ToPage />
</Router>
);
expect(container).toHaveTextContent('from foo');
expect(history.location.state.test).toBe('foo');
});
});