快照测试使用 firebase 函数的组件 - 开玩笑的模拟

Snapshot testing a component that uses firebase functions - Mock in jest

我在 React Native 项目中工作,我想用 Jest 对我的 LoginComponent 进行快照测试。

LoginComponent.js 在这里看到,我正在使用 firebase 的一些函数:

import { StyleSheet, View, Image } from 'react-native'
import React, { useEffect, useState } from 'react'
import { useNavigation } from '@react-navigation/native';
import { getAuth, onAuthStateChanged, signInWithEmailAndPassword } from 'firebase/auth';
import {Title, Button, Headline, Subheading, Text, TextInput} from 'react-native-paper';

export default function LoginComponent() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const navigation = useNavigation();
  const auth = getAuth();


  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        navigation.navigate("Category");
      }
    })
    return unsubscribe
  }, [])


  const handleLogin = () => {
    signInWithEmailAndPassword(auth, email, password)
      .then(userCredentials => {
        const user = userCredentials.user;
        console.log('Logged in with: ', user);
      })
      .catch(error => alert(error.message))
  }


    return (
      <View style={styles.container}>
        <View>
          <Image style={{width: 120, height: 80, resizeMode: 'center'}} source= 
          {require('./41-410195_blue-cloud-clipart.png')}  />
        </View>
        <View>
          <Title style={styles.title}>The Organizer {"\n"}</Title>
        </View>
        <View>
          <Headline>Sign in</Headline>
          <Subheading>Hi there! Nice to see you again</Subheading>
        </View>
        <View>
          <Text style={styles.inputText}>E-mail:</Text>
          <TextInput style={styles.input} placeholder="Enter your email" value={email} 
          onChangeText={text => setEmail(text)}/>
          <Text style={styles.inputText}>Password:</Text>
          <TextInput style={styles.input} secureTextEntry placeholder="Enter your 
           password" 
          value={password} onChangeText={text => setPassword(text)}/>
        </View>
        <View>
            <Button style={styles.buttonLogin} mode="contained" uppercase={false} 
            onPress={handleLogin}>
            Login
            </Button>
        </View>
        <View style={styles.buttonSignUpAndPassword}>
            <Button style={styles.buttonContainer} uppercase={false} onPress={() => 
            {navigation.navigate("SignUp")}}>
            Sign up here
            </Button>
            <View style={styles.space} />
            <Button style={styles.buttonContainer} uppercase={false} onPress={() => 
            {navigation.navigate("ForgotPassword")}}>
              Forgot password?
            </Button>
        </View>
      </View>
    );
  }

  const styles = StyleSheet.create({
    container: {
      flex: 1,
      alignItems: 'center',
      justifyContent: 'center',
      marginTop: 50
    },
    title: {
      fontSize: 32,
      fontWeight: 'bold',
    },
    inputText: {
      paddingLeft: 14,
      paddingTop: 8
    },
    input: {
      padding: 10,
      margin: 15,
      width: 320,
      height: 30,
    },
    buttonLogin: {
      width: 200
    },
    buttonSignUpAndPassword: {
      flexDirection: 'row',
      justifyContent: 'space-between',
      marginTop: 180
    },
    buttonContainer: {
      flex: 1
    },
    space: {
      width: 50,
      height: 20,
    },
  });

我认为我需要创建三个模拟函数,因为 auth、onAuthStateChanged 和 signInWithEmailAndPassword 在我的快照测试可以 运行 成功之前。

这就是我在 login.test.js 中的做法:

import React from 'react';
import renderer from 'react-test-renderer'
import LoginComponent from '../components/Login/LoginComponent';

jest.mock('firebase/auth', () => {
    return {
        auth: jest.fn().mockReturnThis(),
        signInWithEmailAndPassword: jest.fn(() => mockSignIn),
        onAuthStateChanged: jest.fn(() => mockUserState)
    }
})


test('renders loginComponent correctly', () => {
    const tree = renderer.create(<LoginComponent/>).toJSON();
    expect(tree).toMatchSnapshot();
});

这是我收到的错误: The error

我不知道为什么在快照测试时会出现导航错误。我更担心我对 firebase 函数的模拟没有以正确的方式完成。

问题似乎与 useNavigation 挂钩有关。您可能需要

(1) 为 useNavigation 设置模拟:

const mockedNavigate = jest.fn();

jest.mock('@react-navigation/native', () => {
  const actualNav = jest.requireActual('@react-navigation/native');
  return {
    ...actualNav,
    useNavigation: () => ({
      navigate: mockedNavigate,
    })
  };
});

(2) 从 @react-navigation/native 导入 NavigationContainer 并在进行测试时用它包裹你的组件,如下所示:

import { NavigationContainer } from '@react-navigation/native';
...
...
test('renders loginComponent correctly', () => {
    const tree = renderer.create(
      <NavigationContainer>
        <LoginComponent/>
      </NavigationContainer>
    ).toJSON();
    expect(tree).toMatchSnapshot();
});

注意:为简洁起见,以上只是一些虚拟代码。

其他一些considerations/food供思考:

  • 当您模拟 firebase/auth

    时,您可能还必须为 getAuth 设置模拟
  • signInWithEmailAndPassword returns 一个承诺,因此您的模拟必须类似于 jest.mockResolvedValue() 而不仅仅是 jest.fn(),所以希望您的 mockSignIn 价值正在处理

测试愉快!