在我的React-Native项目上,我尝试开始单元测试,如何测试涉及登录提取api调用的Redux动作。我看过一些测试异步Action的示例,但是我没有包装我围绕如何测试下面的代码。
我已经看过使用redux-mock-store,但是我不知道从哪里开始测试。
import {
API_URL_AUTH,
API_URL,
CLIENT_ID,
CLIENT_SECRET,
} from "../config/consts";
import { FETCHING, FETCHED } from "../actions/ActionTypes";
const { dispatch } = this.props;
const { email, password } = this.state;
const authenticationData = {
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
username: email,
password: password,
};
dispatch({ type: FETCHING });
let response = await (await fetch(API_URL_AUTH + "oauth/token", {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify(authenticationData),
})).json();
if (!response.error) {
await AsyncStorage.setItem("accessToken", "Bearer " + response.access_token);
let user = await (await fetch(API_URL + "user", {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + response.access_token,
},
method: "GET",
})).json();
} else {
showMessage({
message: response.message,
type: "danger",
});
}
dispatch({ type: FETCHED });
答案 0 :(得分:0)
我第一次积极回答关于stackoverflow的问题时,我必须注册一个帐户,但是事情就这样了。
单元测试的还原动作完全是关于测试协作的。将协作“单元”测试与每个人都知道的简单的简单单元测试分开总是很聪明的。
我首先将fetch和locale存储调用抽象到它们自己的包装函数中。这有几个优点:
// api.js
import {
API_URL_AUTH,
API_URL,
CLIENT_ID,
CLIENT_SECRET,
} from "../config/consts";
const authenticationData = {
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
};
export const fetchToken = ({ username, password }) => {
return fetch(API_URL_AUTH + "oauth/token", {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({ ...authenticationData, username, password }),
})).json()
}
export fetchUser = token => fetch(API_URL + "user", {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
method: "GET",
})).json();
// localstorage.js
// import { AsyncStorage } from 'some-library' ???
export const storeItem = (key, value)=> AsyncStorage.setItem(key, value);
这使动作的代码更加清晰,并且您想要完成的工作更加直接,最终将使测试变得更容易。
// actions.js
import { fetchToken, fetchUser } from "./api";
import { storeItem } from "./localstorage";
import { FETCHING, FETCHED } from "../actions/ActionTypes";
export const loginUser = ({ username, password }) => async (
dispatch,
getState,
{ signInUser, getCurrentUser }
) => {
dispatch({ type: FETCHING }); // probably needs a better action type
try {
const response = await fetchToken({ username, password });
if (!response.error) {
await storeItem ("accessToken", "Bearer " + response.access_token);
await fetchUser(response.access_token)
} else {
showMessage({
message: response.message,
type: "danger",
});
}
dispatch({ type: FETCHED }); // needs better action type
} catch (e) {
showMessage({
message: response.message,
type: "danger",
});
}
}
请注意,(我相信)Redux中的动作是协作者测试,这意味着它们非常适合模拟功能,因为您仅测试交互,而不是功能的实际逻辑。简单的逻辑函数很容易进行单元测试,包装库的函数(因此您可以控制api)通常根本不会经过测试,或者您可以编写几个集成测试,这些测试独立地测试包装器,但与库本身。
您会看到,通过提取逻辑,动作本身变得非常简单,因此易于测试。
在我使用的testdouble库中要注意的一件事是td.when
也验证了该调用。因此,如果您在代码中无意中调用了fetchToken({ email, password })
之类的东西,则测试将失败的原因有两个:
fetchToken
thenResolve
不会返回包含{ access_token: 'token123' }
的诺言,这意味着fetchUser
也不会使用正确的参数来调用,因为access_token
将是{{1} } undefined
我在github上有一个要点,这是同一原理的另一个示例,因为对于React不和谐的人们来说这是一个常见问题。 您可以在这里找到一个:https://gist.github.com/venikx/6e03367300f47625d5373826e86afeae
我建议您看看https://github.com/testdouble/contributing-tests/wiki/Test-Double,它是测试的绝佳资源
看看https://www.youtube.com/watch?v=Af4M8GMoxi4,它更深入地解释了这些协作类型的测试(有关包装函数,简单逻辑函数以及它们应如何与这些协作函数进行交互的讨论)
关于测试的最重要部分是隔离在重构某些代码时不应中断的行为。测试应该使您可以轻松地重构代码,而不是更难(这就是为什么我们喜欢包装器函数的原因)。