Unit testing Xamarin.Forms MVVMLight应用程序的Xunit测试工作不正常,并且从未停止运行
我正在学习为Xamarin应用程序编写单元测试用例。我使用MVVMLight和ServiceLocator创建了一个非常基本的表单应用程序。我有一个登录页面和一个要测试的LoginViewModel。我已经创建了一个Xunit测试项目来测试LoginViewModel。首先,我创建了一个通过测试(事实),检查测试项目是否正常工作。它运行良好,通过顺利 在那之后,我用InlineData编写了另一个理论来测试我的登录命令的功能,在那里发生了错误,为登录命令编写的测试从未停止运行,也没有通过或失败测试 我在下面提到我的两门课,如果有人能看一下,告诉我我做错了什么Unit testing Xamarin.Forms MVVMLight应用程序的Xunit测试工作不正常,并且从未停止运行,unit-testing,xamarin,xamarin.forms,mvvm-light,xunit,Unit Testing,Xamarin,Xamarin.forms,Mvvm Light,Xunit,我正在学习为Xamarin应用程序编写单元测试用例。我使用MVVMLight和ServiceLocator创建了一个非常基本的表单应用程序。我有一个登录页面和一个要测试的LoginViewModel。我已经创建了一个Xunit测试项目来测试LoginViewModel。首先,我创建了一个通过测试(事实),检查测试项目是否正常工作。它运行良好,通过顺利 在那之后,我用InlineData编写了另一个理论来测试我的登录命令的功能,在那里发生了错误,为登录命令编写的测试从未停止运行,也没有通过或失败测
public class LoginViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the <see cref="T:MVVMArchitecture.ViewModels.LoginViewModel"/> class.
/// </summary>
/// <param name="navigation"> Navigation </param>
/// <param name="dialog"> Dialog</param>
public LoginViewModel(INavigationService navigation, IDialogService dialog) : base(navigation, dialog)
{
Title = PageTitles.LoginTitle;
Validator = new UserValidator();
ValidationContext = new ValidationContext<User>(User, new PropertyChain(), new RulesetValidatorSelector(new[] { "Login", "Password" }));
}
#region Properties
User user = new User();
public User User
{
get => user;
set { SetProperty(ref user, value); }
}
RelayCommand loginCommand;
public RelayCommand LoginCommand
{
get => loginCommand ?? (loginCommand = new RelayCommand(LoginUser, () => !IsBusy));
}
#endregion
#region Action Hanlders
async void LoginUser()
{
if (IsBusy)
return;
IsBusy = true;
LoginCommand.RaiseCanExecuteChanged();
var validationResults = Validator.Validate(ValidationContext);
if (validationResults.IsValid)
{
var loggedIn = await LoginFlow();
if (loggedIn)
{
Device.BeginInvokeOnMainThread(() =>
{
var navPage = new NavigationPage(new HomePage())
{
BarBackgroundColor = (Color)Application.Current.Resources["AppThemeColor"],
BarTextColor = Color.White
};
App.ConfigureMainPage(navPage);
});
}
else
{
IsBusy = false;
await DialogService.ShowMessage(AuthenticationAlerts.InvalidCredentials, AuthenticationAlerts.LoginFailed);
}
}
else
{
IsBusy = false;
await DialogService.ShowMessage(validationResults.Errors[0].ErrorMessage, AuthenticationAlerts.LoginFailed);
}
IsBusy = false;
LoginCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// Login flow to login user based on internet and service mode.
/// </summary>
/// <returns>The flow.</returns>
async Task<bool> LoginFlow()
{
return await Task.Run(async () => CommonUtils.Instance.IsConnected ? await ServiceManager.Instance.LoginUser(User) : await DataManager.Instance.LoginUser(User));
}
#endregion
// sample method call from code behind
public void DisplayData(string msg)
{
DialogService?.ShowMessageBox(msg, AppConstants.LoginPage);
}
}
public类LoginViewModel:ViewModelBase
{
///
///初始化类的新实例。
///
///航行
///对话
public LoginView模型(INavigationService导航,IDialogService对话框):基础(导航,对话框)
{
Title=PageTitles.LoginTitle;
Validator=新的UserValidator();
ValidationContext=new ValidationContext(用户,new PropertyChain(),new RulesetValidatorSelector(new[]{“Login”,“Password”}));
}
#区域属性
用户=新用户();
公共用户
{
get=>user;
set{SetProperty(ref user,value);}
}
RelayCommand逻辑命令;
公共中继命令登录命令
{
get=>loginCommand??(loginCommand=newrelaycommand(logiuser,()=>!IsBusy));
}
#端区
#区域行动汉德尔
异步void登录用户()
{
如果(忙)
返回;
IsBusy=true;
LoginCommand.RaiseCanExecuteChanged();
var validationResults=Validator.Validate(ValidationContext);
if(validationResults.IsValid)
{
var loggedIn=await LoginFlow();
if(loggedIn)
{
Device.beginInvokeMainThread(()=>
{
var navPage=新导航页面(新主页())
{
BarBackgroundColor=(Color)Application.Current.Resources[“AppThemeColor”],
BarTextColor=颜色。白色
};
应用程序配置主页(导航页);
});
}
其他的
{
IsBusy=false;
等待DialogService.ShowMessage(AuthenticationAlerts.InvalidCredentials,AuthenticationAlerts.LoginFailed);
}
}
其他的
{
IsBusy=false;
等待DialogService.ShowMessage(validationResults.Errors[0].ErrorMessage,AuthenticationAlerts.LoginFailed);
}
IsBusy=false;
LoginCommand.RaiseCanExecuteChanged();
}
///
///基于internet和服务模式的登录用户登录流程。
///
///流动。
异步任务登录流()
{
返回wait Task.Run(async()=>CommonUtils.Instance.IsConnected?wait ServiceManager.Instance.LoginUser(用户):wait DataManager.Instance.LoginUser(用户));
}
#端区
//来自代码隐藏的示例方法调用
公共无效显示数据(字符串消息)
{
DialogService?.ShowMessageBox(消息,AppConstants.LoginPage);
}
}
我的测试班:
public class UnitTest1
{
App app;
LoginViewModel loginViewModel;
public UnitTest1()
{
Models.TestingMocks.Init();
app = new App();
loginViewModel = new LoginViewModel(ServiceLocator.Current.GetInstance<INavigationService>(),
ServiceLocator.Current.GetInstance<IDialogService>());
}
[Fact]
public void MustPass()
{
int expected = 1;
int actual = 1;
Assert.Equal(expected, actual);
Assert.NotNull(app);
Assert.NotNull(loginViewModel);
}
[Theory]
[InlineData("ACosta", "Time2Eat!")]
public void EmptyUsernamePassword(string username, string password)
{
var model = CreateViewModelAndLogin(username, password);
//var v = ServiceManager.Instance.LoginUser(model.User);
//A.CallTo(() => v.Result).MustHaveHappened();
Assert.NotNull(model);
}
private LoginViewModel CreateViewModelAndLogin(string userName, string password)
{
loginViewModel.User = new User
{
UserName = userName,
Password = password
};
loginViewModel.LoginCommand.Execute(null);
return loginViewModel;
}
}
公共类UnitTest1
{
应用程序;
LoginView模型LoginView模型;
公共单元测试1()
{
Models.TestingMocks.Init();
app=新app();
loginViewModel=新的loginViewModel(ServiceLocator.Current.GetInstance(),
ServiceLocator.Current.GetInstance());
}
[事实]
公共空间通行证()
{
int预期为1;
int实际值=1;
断言。相等(预期、实际);
Assert.NotNull(app);
Assert.NotNull(loginViewModel);
}
[理论]
[InlineData(“ACosta”、“Time2Eat!”)]
public void EmptyUsernamePassword(字符串用户名、字符串密码)
{
var model=CreateViewModelAndLogin(用户名、密码);
//var v=ServiceManager.Instance.LoginUser(model.User);
//A.CallTo(()=>v.Result)。必须发生();
Assert.NotNull(模型);
}
private LoginViewModel CreateViewModelAndLogin(字符串用户名、字符串密码)
{
loginViewModel.User=新用户
{
用户名=用户名,
密码=密码
};
loginViewModel.LoginCommand.Execute(null);
返回loginViewModel;
}
}
谢谢 为什么要使用
异步void
?(注意async void
最适合异步事件处理程序)这看起来更像是一个集成测试,而不是一个基于视图模型紧密耦合设计的独立单元测试,以及测试时访问的内容。@Nkosi如何使其松散耦合或单元测试而不是集成测试?我使用MVVMLight使应用程序松散耦合,但我对它不熟悉。将登录流提取到它自己的关注点/服务中。还将平台问题(即设备。*)提取到其自身的问题/服务中。显式地将提取的关注点注入视图模型。重构命令以使用异步事件处理程序而不是普通的异步void方法。为什么要使用async void
?(注意async void
最适合异步事件处理程序)这看起来更像是一个集成测试,而不是一个基于视图模型紧密耦合设计的独立单元测试,以及测试时访问的内容。@Nkosi如何使其松散耦合或单元测试而不是集成测试?我使用MVVMLight使应用程序松散耦合,但我对它不熟悉。将登录流提取到它自己的关注点/服务中。还将平台问题(即设备。*)提取到其自身的问题/服务中。显式地将提取的关注点注入视图模型。重构命令以使用异步事件处理程序而不是普通的异步v