我正在尝试在我的React Native应用上实施e2e测试。
Detox构建在Android和iOS上均成功,并且我设法按预期运行了模拟程序,但是Detox似乎无法在我要测试的页面上找到任何组件。我要查找的所有组件都具有一个testId,并且当我尝试在Android和iOS上进行测试时,ID都存在于视图层次结构中。
这是我在android上收到的错误:
androidx.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'at least 75 percent of the view's area is displayed to the user.' doesn't match the selected view.
Expected: at least 75 percent of the view's area is displayed to the user.
Got: null
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:1538)
at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:96)
at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:59)
at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:324)
at androidx.test.espresso.ViewInteraction.check(ViewInteraction.java:306)
at com.wix.detox.espresso.DetoxAssertion.assertMatcher(DetoxAssertion.java:32)
at java.lang.reflect.Method.invoke(Native Method)
at org.apache.commons.lang3.reflect.MethodUtils.invokeStaticMethod(MethodUtils.java:443)
at org.apache.commons.lang3.reflect.MethodUtils.invokeStaticMethod(MethodUtils.java:405)
at com.wix.invoke.types.ClassTarget.execute(ClassTarget.java:23)
at com.wix.invoke.types.Target.invoke(Target.java:59)
at com.wix.invoke.MethodInvocation.invoke(MethodInvocation.java:35)
at com.wix.invoke.MethodInvocation.invoke(MethodInvocation.java:26)
at com.wix.invoke.MethodInvocation.invoke(MethodInvocation.java:20)
at com.wix.detox.InvokeActionHandler.handle(DetoxActionHandlers.kt:52)
at com.wix.detox.DetoxManager$4.run(DetoxManager.java:121)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at com.wix.detox.Detox$1.run(Detox.java:135)
at java.lang.Thread.run(Thread.java:764)
Caused by: junit.framework.AssertionFailedError: 'at least 75 percent of the view's area is displayed to the user.' doesn't match the selected view.
Expected: at least 75 percent of the view's area is displayed to the user.
Got: null
at androidx.test.espresso.matcher.ViewMatchers.assertThat(ViewMatchers.java:540)
at com.wix.detox.espresso.assertion.ViewAssertions$MatchesViewAssertion.check(ViewAssertions.java:52)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAssertion.check(ViewInteraction.java:425)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:288)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:272)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Check device logs for full details!
9 |
10 | it('should have physiotherapy button', async () => {
> 11 | await expect(element(by.id('Physiotherapy button'))).toBeVisible();
| ^
12 | });
13 |
14 | it('should have reminders button', async () => {
at Client.execute (../node_modules/detox/src/client/Client.js:92:28)
at InvocationManager.execute (../node_modules/detox/src/invoke.js:11:39)
at MatcherAssertionInteraction.execute (../node_modules/detox/src/android/expect.js:128:35)
at ExpectElement.toBeVisible (../node_modules/detox/src/android/expect.js:275:112)
at _callee3$ (dashboard.spec.js:11:61)
at tryCatch (../node_modules/regenerator-runtime/runtime.js:45:40)
at Generator.invoke [as _invoke] (../node_modules/regenerator-runtime/runtime.js:271:22)
at Generator.prototype.<computed> [as next] (../node_modules/regenerator-runtime/runtime.js:97:21)
at tryCatch (../node_modules/regenerator-runtime/runtime.js:45:40)
at invoke (../node_modules/regenerator-runtime/runtime.js:135:20)
at ../node_modules/regenerator-runtime/runtime.js:170:11
at callInvokeWithMethodAndArg (../node_modules/regenerator-runtime/runtime.js:169:16)
at AsyncIterator.enqueue (../node_modules/regenerator-runtime/runtime.js:192:13)
at AsyncIterator.prototype.<computed> [as next] (../node_modules/regenerator-runtime/runtime.js:97:21)
at Object.exports.async (../node_modules/regenerator-runtime/runtime.js:216:14)
这是测试:
describe('Dashboard', () => {
beforeEach(async () => {
await device.reloadReactNative();
});
it('should have physiotherapy button', async () => {
await expect(element(by.id('Physiotherapy button'))).toBeVisible();
});
这是我要测试的组件:
/* Dashboard with custom buttons to navigate between pages */
import React, {Component} from 'react';
import {View, StyleSheet} from 'react-native';
import SplashScreen from 'react-native-splash-screen';
import DashboardButton from '../layout/DashboardButton';
import {connect} from 'react-redux';
import * as actions from '../../actions/index';
import {NavigationEvents} from 'react-navigation';
export class Dashboard extends Component {
constructor(props) {
super(props);
// present log in page if user is not logged in
if (props.loggedIn !== true) {
this.login();
}
// show dashboard
SplashScreen.hide();
}
login() {
this.props.addDevice('testId', 'testToken');
this.props.loginUser('testId', 'testToken');
this.props.loadInitialReminders();
this.props.loadInitialDiaryEntries();
}
render() {
return (
<View testId={'Dashboard'} accessible={true} style={styles.mainContainer}>
<NavigationEvents
onDidFocus={() => {
if (this.props.loggedIn !== true) {
this.login();
}
}}
/>
<DashboardButton
testId={'Physiotherapy button'}
accessibilityLabel={'Physiotherapy button'}
accessibilityHint={
'Navigates to the Physiotherapy exercise categories screen'
}
disabled={!this.props.loggedIn}
title="PHYSIOTHERAPY"
customClick={() =>
this.props.navigation.navigate('PhysiotherapyExerciseCategories')
}
/>
<DashboardButton
testId={'Reminders button'}
accessibilityLabel={'Reminders button'}
accessibilityHint={'Navigates to the Reminders screen'}
disabled={!this.props.loggedIn}
title="REMINDERS"
customClick={() => this.props.navigation.navigate('Reminders')}
/>
<DashboardButton
testId={'Diary button'}
accessibilityLabel={'Diary button'}
accessibilityHint={'Navigates to the Diary screen'}
disabled={!this.props.loggedIn}
title="DIARY"
customClick={() => this.props.navigation.navigate('Diary')}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
loggedIn: state.authorisationReducer.loggedIn,
reminders: state.remindersReducer.reminders,
notificationsSet: state.remindersReducer.notificationsSet,
};
};
export default connect(
mapStateToProps,
actions,
)(Dashboard);
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
backgroundColor: 'white',
flexDirection: 'column',
},
});
如您所见,我正在寻找一个自定义组件,但该组件只是包裹在一个具有proptestId = {props.testId}的TouchableOpacity周围。我还尝试过为Dashboard组件中的主视图提供ID并进行搜索,但仍然没有运气。
我尝试按标签和ID搜索。
我还读到这可能是一个本机反应,因为它们嵌套了组件,因此排毒将无法找到该组件。是这样吗这是我的package.json文件,向您显示我正在使用的版本:
{
"name": "XXX",
"version": "1.0.0",
"private": true,
"description": "XXX",
"author": "XXX",
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
"@bam.tech/react-native-make": "^1.0.3",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/react-native-fontawesome": "^0.1.0",
"@react-native-community/async-storage": "^1.8.1",
"@react-native-community/cli": "^4.0.0",
"@react-native-community/cli-platform-android": "^4.6.3",
"@react-native-community/datetimepicker": "^2.2.1",
"@react-native-community/netinfo": "^5.5.1",
"axios": "^0.18.1",
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"classnames": "^2.2.6",
"concurrently": "^4.1.2",
"enzyme-adapter-react-16": "^1.15.2",
"express": "^4.17.1",
"is-empty": "^1.2.0",
"jetifier": "^1.6.5",
"jsonwebtoken": "^8.5.1",
"jwt-decode": "^2.2.0",
"lodash.uniqueid": "^4.0.1",
"moment": "^2.24.0",
"mongoose": "^5.7.12",
"node-schedule": "^1.3.2",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0",
"react": "16.9.0",
"react-dom": "^16.12.0",
"react-native": "0.61.5",
"react-native-auth0": "^2.3.0",
"react-native-base64": "0.0.2",
"react-native-elements": "^1.2.7",
"react-native-firebase": "^5.6.0",
"react-native-gesture-handler": "^1.5.0",
"react-native-modal": "^11.5.4",
"react-native-modal-selector": "^1.1.5",
"react-native-paper": "^3.2.1",
"react-native-render-html": "^4.2.0",
"react-native-screens": "^2.0.0-beta.2",
"react-native-splash-screen": "^3.0.6",
"react-native-svg": "^11.0.1",
"react-native-uuid-generator": "^6.1.1",
"react-native-vector-icons": "^6.6.0",
"react-native-webview": "^8.1.2",
"react-navigation": "^4.0.10",
"react-navigation-stack": "^1.10.3",
"react-redux": "^5.1.2",
"react-router-dom": "^4.3.1",
"react-scripts": "^3.3.0",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0",
"serialize-javascript": "^2.1.2",
"typescript": "^3.7.2",
"validator": "^10.11.0",
"xss": "^1.0.6"
},
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/preset-env": "^7.1.0",
"@babel/preset-flow": "^7.8.3",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.6.2",
"@bam.tech/react-native-make": "^1.0.3",
"@react-native-community/eslint-config": "^0.0.5",
"babel-jest": "^24.9.0",
"detox": "^16.1.1",
"dotenv-webpack": "^1.7.0",
"enzyme": "^3.11.0",
"eslint": "^6.5.1",
"jest": "^25.3.0",
"metro-react-native-babel-preset": "^0.56.0",
"nodemon": "^2.0.0",
"react-test-renderer": "16.9.0",
"redux-mock-store": "^1.5.4"
},
"jest": {
"preset": "react-native",
"setupFiles": [
"./node_modules/react-native-gesture-handler/jestSetup.js",
"./node_modules/react-test-renderer/cjs/react-test-renderer.development.js"
],
"setupFilesAfterEnv": [
"./__mocks__/async-storage.js",
"./__mocks__/auth0.js",
"./__mocks__/firebase.js",
"./__mocks__/net-info.js",
"./__mocks__/react-native-navigation.js",
"./__mocks__/react-native-splash-screen.js"
],
"transformIgnorePatterns": [
"./node_modules/?!(react-navigation|react-native-gesture-handler)"
]
},
"detox": {
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/Tulip.app",
"build": "RN_SRC_EXT=e2e.js xcodebuild -workspace ios/clean.xcworkspace -scheme Tulip -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
"type": "iPhone 11 Pro"
}
},
"android.emu.debug": {
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build": "RN_SRC_EXT=e2e.js cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Pixel_2_XL_API_27"
}
},
"android.emu.release": {
"binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
"build": "RN_SRC_EXT=e2e.js cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Pixel_2_XL_API_27"
}
}
},
"test-runner": "jest"
}
}
如果您知道解决此问题的方法或另一种e2e测试方法,请告诉我。预先感谢您的帮助。
答案 0 :(得分:0)
我很傻。错字。
测试ID应该写为:testID =“ Dashboard”