- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
tl;博士:在单元测试中,@Inject
成功注入(inject)所需字段,但 @InjectView
将它们保留为空,从而导致错误。 @InjectView
在正常的应用程序运行中工作。
我有一个使用 RoboGuice 的 Android 应用程序,我正在尝试对其进行单元测试。我尝试测试的一项 Activity 如下所示:
@ContentView(R.layout.activity_login)
public class LoginActivity extends RoboActivity {
/**
* Our injected API class.
*/
@Inject
private Api apiConn;
/**
* Main application state class.
*/
@Inject
private Application appState;
/**
* Keep track of the login task to ensure we can cancel it if requested.
*/
private UserLoginTask mAuthTask = null;
/**
* Logo header
*/
@InjectView(R.id.logoHeader)
private TextView mLogoHeader;
/**
* Signin button.
*/
@InjectView(R.id.email_sign_in_button)
private Button mEmailSignInButton;
/**
* Email text view.
*/
@InjectView(R.id.email)
private EditText mEmailView;
/**
* Password text view.
*/
@InjectView(R.id.password)
private EditText mPasswordView;
/**
* Login progress view.
*/
@InjectView(R.id.login_progress)
private View mProgressView;
/**
* Login form view.
*/
@InjectView(R.id.login_form)
private View mLoginFormView;
/**
* Fired when Activity has been requested.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check if the server is actually online
PingTask ping = new PingTask();
ping.execute();
// Check if the user is already logged in.
if (appState.hasLoginToken() && appState.getLoginToken().isValid()) {
// Hide everything!
mLogoHeader.setVisibility(View.INVISIBLE);
mEmailView.setVisibility(View.INVISIBLE);
mPasswordView.setVisibility(View.INVISIBLE);
mEmailSignInButton.setVisibility(View.INVISIBLE);
// The user already has a token, let's validate it and pull in extra information.
AuthenticateTask authenticate = new AuthenticateTask();
authenticate.execute();
return;
}
appState.clearSettings();
// Set up the login form.
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == R.id.login || id == EditorInfo.IME_NULL) {
attemptLogin();
return true;
}
return false;
}
});
mEmailSignInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
attemptLogin();
}
});
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
void attemptLogin() {
if (mAuthTask != null) {
return;
}
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password, if the user entered one.
if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}
// Check for a valid email address.
if (TextUtils.isEmpty(email)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
cancel = true;
} else if (!isEmailValid(email)) {
mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
cancel = true;
}
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
showProgress(true);
mAuthTask = new UserLoginTask(email, password);
mAuthTask.execute((Void) null);
}
}
/**
* Checks if a given email address is valid.
*
* @param email Email address to check.
*
* @return Whether the email is valid or not.
*/
private boolean isEmailValid(String email) {
String ePattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
Pattern p = Pattern.compile(ePattern);
Matcher m = p.matcher(email);
return m.matches();
}
/**
* Checks if a given password is valid.
*
* @param password Password to check.
*
* @return Whether the password is valid or not.
*/
private boolean isPasswordValid(CharSequence password) {
//TODO: Is this needed?
return password.length() > 4;
}
/**
* Shows the progress UI and hides the login form.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
void showProgress(final boolean show) {
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
// for very easy animations. If available, use these APIs to fade-in
// the progress spinner.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
mLoginFormView.animate().setDuration(shortAnimTime).alpha(
show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
});
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mProgressView.animate().setDuration(shortAnimTime).alpha(
show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
}
});
} else {
// The ViewPropertyAnimator APIs are not available, so simply show
// and hide the relevant UI components.
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
/**
* Stop hardware back button functionality.
*/
@Override
public void onBackPressed() {
}
/**
* User Login Task.
*/
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
/**
* Email to login with.
*/
private final String mEmail;
/**
* Password to login with.
*/
private final String mPassword;
/**
* Constructor.
*
* @param email Email to login with.
* @param password Password to login with.
*/
public UserLoginTask(String email, String password) {
mEmail = email;
mPassword = password;
}
/**
* Main task.
*
* @param params Parameters.
*
* @return Whether login was successful or not.
*/
@Override
protected Boolean doInBackground(Void... params) {
JSONObject responseJson;
boolean success;
String token;
JSONArray drivers;
JSONArray moods;
JSONArray vehicles;
try {
// Contact API.
String response = apiConn.login(mEmail, mPassword);
responseJson = new JSONObject(response);
success = responseJson.getBoolean("success");
JSONObject data = responseJson.getJSONObject("data");
token = data.getString("token");
drivers = data.getJSONArray("drivers");
moods = data.getJSONArray("moods");
vehicles = data.getJSONArray("vehicles");
} catch (ApiException e) {
return false;
} catch (JSONException e) {
return false;
}
if (!success) {
return false;
}
// Persist data.
appState.setLoginToken(token);
appState.setDrivers(drivers);
appState.setMoods(moods);
appState.setVehicles(vehicles);
return true;
}
/**
* Fired after the task has executed.
*
* @param success Whether the task was successful.
*/
@Override
protected void onPostExecute(final Boolean success) {
mAuthTask = null;
showProgress(false);
if (success) {
// Start chooser activity
Intent chooserIntent = new Intent(getApplicationContext(), ChooserActivity.class);
startActivity(chooserIntent);
// Close task.
finish();
} else {
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
/**
* Fired when the task was cancelled.
*/
@Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
}
/**
* Ping task to test if API is available.
*/
public class PingTask extends AsyncTask<Void, Void, Boolean> {
/**
* Alert dialog.
*/
private AlertDialog.Builder alertDialog;
/**
* Fired before executing task.
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
alertDialog = new AlertDialog.Builder(LoginActivity.this);
}
/**
* Main task.
*
* @param params Parameters.
*
* @return Whether the API is online or not.
*/
@Override
protected Boolean doInBackground(Void... params) {
boolean webOnline = apiConn.isWebOnline();
Log.d("API", String.valueOf(webOnline));
return webOnline;
}
/**
* Fired after task was executed.
*
* @param success Whether the task was successful.
*/
@Override
protected void onPostExecute(final Boolean success) {
super.onPostExecute(success);
Log.d("API", String.valueOf(success));
if (!success) {
alertDialog.setTitle(getString(R.string.cannot_connect));
alertDialog.setMessage(getString(R.string.cannot_connect_long));
AlertDialog dialog = alertDialog.create();
dialog.show();
}
}
}
/**
* Authenticate with existing token task.
*/
class AuthenticateTask extends AsyncTask<Void, Void, Boolean> {
/**
* Array of drivers from Api.
*/
private JSONArray drivers;
/**
* Array of moods from Api.
*/
private JSONArray moods;
/**
* Array of vehicle from Api.
*/
private JSONArray vehicles;
/**
* Main task.
*
* @param params Parameters.
*
* @return Whether the task was successful or not.
*/
@Override
protected Boolean doInBackground(Void... params) {
JSONObject responseJson;
boolean success;
drivers = null;
moods = null;
vehicles = null;
try {
String response = apiConn.authenticate(appState.getLoginToken());
responseJson = new JSONObject(response);
success = responseJson.getBoolean("success");
JSONObject data = responseJson.getJSONObject("data");
drivers = data.getJSONArray("drivers");
moods = data.getJSONArray("moods");
vehicles = data.getJSONArray("vehicles");
} catch (ApiException | JSONException e) {
success = false;
}
return success;
}
/**
* Fired after the task has been executed.
*
* @param success Whether the task was successful.
*/
@Override
protected void onPostExecute(Boolean success) {
super.onPostExecute(success);
if (!success) {
Log.d("JWT", "Token Invalid");
// Token is invalid! Clear and show login form.
appState.clearSettings();
// Show the form again.
mLogoHeader.setVisibility(View.VISIBLE);
mEmailView.setVisibility(View.VISIBLE);
mPasswordView.setVisibility(View.VISIBLE);
mEmailSignInButton.setVisibility(View.VISIBLE);
return;
}
appState.setDrivers(drivers);
appState.setMoods(moods);
appState.setVehicles(vehicles);
Toast toast = Toast.makeText(getApplicationContext(), getString(R.string.welcome_back), Toast.LENGTH_SHORT);
toast.show();
// Start chooser activity
Intent chooserIntent = new Intent(LoginActivity.this, ChooserActivity.class);
startActivity(chooserIntent);
}
}
}
onCreate
方法崩溃说
mPasswordView
在这一行为空:
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
null
.它应该由 RoboGuice 的
@InjectView
设置。当我在我的设备上使用应用程序时有效但不适用于单元测试的注释。
@Inject
然而正在工作......
@InjectView
解析 View ?
@Config(manifest = "app/src/main/AndroidManifest.xml", emulateSdk = 18, reportSdk = 18)
@RunWith(RobolectricGradleTestRunner.class)
public class LoginActivityTest {
private final Api apiMock = mock(Api.class);
private final Application appMock = mock(Application.class);
private final ActivityController<LoginActivity> controller = buildActivity(LoginActivity.class);
@Before
public void setup() {
RoboGuice.overrideApplicationInjector(Robolectric.application, new LoginTestModule());
}
@After
public void teardown() {
RoboGuice.Util.reset();
}
@Test
public void createTriggersPing() {
ActivityController controller = Robolectric.buildActivity(LoginActivity.class);
controller.create();
controller.start();
verify(apiMock).isWebOnline();
}
private class LoginTestModule extends AbstractModule {
@Override
protected void configure() {
bind(Api.class).toInstance(apiMock);
bind(Application.class).toInstance(appMock);
}
}
}
public class RobolectricGradleTestRunner extends RobolectricTestRunner {
public RobolectricGradleTestRunner(Class<?> testClass) throws org.junit.runners.model.InitializationError {
super(testClass);
}
@Override
protected AndroidManifest getAppManifest(Config config) {
String manifestProperty = System.getProperty("android.manifest");
if (config.manifest().equals(Config.DEFAULT) && manifestProperty != null) {
String resProperty = System.getProperty("android.resources");
String assetsProperty = System.getProperty("android.assets");
return new AndroidManifest(Fs.fileFromPath(manifestProperty), Fs.fileFromPath(resProperty),
Fs.fileFromPath(assetsProperty));
}
return super.getAppManifest(config);
}
}
最佳答案
事实证明,这与 Roboguice 本身无关。 OP 的设置存在一些特定的错误配置。通过聊天解决了。问题是使用 Roboguice 的 @ContentView
(它显然不能很好地用于测试,我以前也遇到过同样的情况)和一个配置错误的 XML 文件。
关于android - InjectView 在单元测试中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28840439/
我获得了一些源代码示例,我想测试一些功能。不幸的是,我在执行程序时遇到问题: 11:41:31 [linqus@ottsrvafq1 example]$ javac -g test/test.jav
我想测试ggplot生成的两个图是否相同。一种选择是在绘图对象上使用all.equal,但我宁愿进行更艰巨的测试以确保它们相同,这似乎是identical()为我提供的东西。 但是,当我测试使用相同d
我确实使用 JUnit5 执行我的 Maven 测试,其中所有测试类都有 @ExtendWith({ProcessExtension.class}) 注释。如果是这种情况,此扩展必须根据特殊逻辑使测试
在开始使用 Node.js 开发有用的东西之前,您的流程是什么?您是否在 VowJS、Expresso 上创建测试?你使用 Selenium 测试吗?什么时候? 我有兴趣获得一个很好的工作流程来开发我
这个问题已经有答案了: What is a NullPointerException, and how do I fix it? (12 个回答) 已关闭 3 年前。 基于示例here ,我尝试为我的
我正在考虑测试一些 Vue.js 组件,作为 Laravel 应用程序的一部分。所以,我有一个在 Blade 模板中使用并生成 GET 的组件。在 mounted 期间请求生命周期钩子(Hook)。假
考虑以下程序: #include struct Test { int a; }; int main() { Test t=Test(); std::cout<
我目前的立场是:如果我使用 web 测试(在我的例子中可能是通过 VS.NET'08 测试工具和 WatiN)以及代码覆盖率和广泛的数据来彻底测试我的 ASP.NET 应用程序,我应该不需要编写单独的
我正在使用 C#、.NET 4.7 我有 3 个字符串,即。 [test.1, test.10, test.2] 我需要对它们进行排序以获得: test.1 test.2 test.10 我可能会得到
我有一个 ID 为“rv_list”的 RecyclerView。单击任何 RecyclerView 项目时,每个项目内都有一个可见的 id 为“star”的 View 。 我想用 expresso
我正在使用 Jest 和模拟器测试 Firebase 函数,尽管这些测试可能来自竞争条件。所谓 flakey,我的意思是有时它们会通过,有时不会,即使在同一台机器上也是如此。 测试和函数是用 Type
我在测试我与 typeahead.js ( https://github.com/angular-ui/bootstrap/blob/master/src/typeahead/typeahead.js
我正在尝试使用 Teamcity 自动运行测试,但似乎当代理编译项目时,它没有正确完成,因为当我运行运行测试之类的命令时,我收到以下错误: fatal error: 'Pushwoosh/PushNo
这是我第一次玩 cucumber ,还创建了一个测试和 API 的套件。我的问题是在测试 API 时是否需要运行它? 例如我脑子里有这个, 启动 express 服务器作为后台任务 然后当它启动时(我
我有我的主要应用程序项目,然后是我的测试的第二个项目。将所有类型的测试存储在该测试项目中是一种好的做法,还是应该将一些测试驻留在主应用程序项目中? 我应该在我的主项目中保留 POJO JUnit(测试
我正在努力弄清楚如何实现这个计数。模型是用户、测试、等级 用户 has_many 测试,测试 has_many 成绩。 每个等级都有一个计算分数(strong_pass、pass、fail、stron
我正在尝试测试一些涉及 OkHttp3 的下载代码,但不幸失败了。目标:测试 下载图像文件并验证其是否有效。平台:安卓。此代码可在生产环境中运行,但测试代码没有任何意义。 产品代码 class Fil
当我想为 iOS 运行 UI 测试时,我收到以下消息: SetUp : System.Exception : Unable to determine simulator version for X 堆
我正在使用 Firebase Remote Config 在 iOS 上设置 A/B 测试。 一切都已设置完毕,我正在 iOS 应用程序中读取服务器端默认值。 但是在多个模拟器上尝试,它们都读取了默认
[已编辑]:我已经用 promise 方式更改了我的代码。 我正在写 React with this starter 由 facebook 创建,我是测试方面的新手。 现在我有一个关于图像的组件,它有
我是一名优秀的程序员,十分优秀!