![](/img/trans.png)
[英]How to write a generic mock which maps interface properties to key-value pairs in c# using moq
[英]Mock interface implementation using Moq in C#
我正在尝试使用 Moq 编写 Xamarin.Forms UI 测试来模拟我的身份验证界面:[上一个问题][1]。 我重构了我的应用程序,以便我的SignIn(string username, string password)
方法位于实现 IAuthService 的IAuthService
。 我现在遇到了 mocking IAuthService
的问题,基本上“替换”了单击Sign In
按钮时发生的实际登录验证。 在我的CloudAuthService
class (实现IAuthService
)中,我正在对 Amazon Cognito 进行身份验证,但我想在 UI 测试中模拟此结果,因此它不会调用云服务。
编辑:经过很多建议,我决定将我当前的实现包括在下面。 尽管来自Console.WriteLine(App.AuthApi.IsMockService());
在BeforeEachTest()
方法中结果为true
(如预期的那样)。 但是,在App()
构造函数方法中运行相同的东西会导致false
。 所以在应用程序实际启动之前它似乎没有运行,有没有办法让 UITest 代码在应用程序初始化之前运行?
登录页面
[XamlCompilation(XamlCompilationOptions.Compile)]
public sealed partial class LoginPage
{
private readonly IBiometricAuthentication _bioInterface;
private static readonly Lazy<LoginPage>
Lazy =
new Lazy<LoginPage>
(() => new LoginPage(App.AuthApi));
public static LoginPage Instance => Lazy.Value;
private string _username;
private string _password;
private LoginPageViewModel _viewModel;
private IAuthService _authService;
public LoginPage(IAuthService authService)
{
InitializeComponent();
_authService = authService;
_viewModel = new LoginPageViewModel();
BindingContext = _viewModel;
}
private void LoginButtonClicked(object sender, EventArgs args)
{
_username = UsernameEntry.Text;
_password = PasswordEntry.Text;
LoginToApplication();
}
public async void LoginToApplication()
{
AuthenticationContext context = await _authService.SignIn(_username, _password);
}
}
应用程序 Class
public partial class App
{
public static IAuthService AuthApi { get; set; } = new AWSCognito()
public App()
{
Console.WriteLine(AuthApi.IsMockService())
// AuthApi = new AWSCognito(); // AWSCognito implements IAuthService
InitializeComponent();
MainPage = new NavigationPage(new LoginPage(AuthApi));
}
}
测试 Class
class LoginPageTest
{
IApp app;
readonly Platform platform;
public LoginPageTest(Platform platform)
{
this.platform = platform;
}
[SetUp]
public void BeforeEachTest()
{
var mocker = new Mock<IAuthService>();
var response = new AuthenticationContext(CognitoResult.Ok)
{
IdToken = "SUCCESS_TOKEN"
};
mocker.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(() => new MockAuthService().SignIn("a", "a"));
mocker.Setup(x => x.IsMockService()).Returns(() => new MockAuthService().IsMockService());
App.AuthApi = mocker.Object;
Console.WriteLine(App.AuthApi.IsMockService());
app = AppInitializer.StartApp(platform);
}
[Test]
public void ClickingLoginWithUsernameAndPasswordStartsLoading()
{
app.WaitForElement(c => c.Marked("Welcome"));
app.EnterText(c => c.Marked("Username"), new string('a', 1));
app.EnterText(c => c.Marked("Password"), new string('a', 1));
app.Tap("Login");
bool state = app.Query(c => c.Class("ProgressBar")).FirstOrDefault().Enabled;
Assert.IsTrue(state);
}
}
您的问题似乎是您在完成测试后注入了模拟。 这意味着它在执行时使用原始的 AuthService。 如果我们在执行任何操作之前重新排列代码以移动注入,我们应该会看到我们期望的结果:
// let's bring this mock injection up here
var mocker = new Mock<IAuthService>();
mocker.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(response)).Verifiable();
App.AuthApi = mocker.Object;
// now we try to login, which should call the mock methods of the auth service
app.WaitForElement(c => c.Marked("Welcome to Manuly!"));
app.EnterText(c => c.Marked("Username"), new string('a', 1));
app.EnterText(c => c.Marked("Password"), new string('a', 1));
app.Tap("Login");
var response = new AuthenticationContext(CognitoResult.Ok)
{
IdToken = "SUCCESS_TOKEN",
};
bool state = app.Query(c => c.Class("ProgressBar")).FirstOrDefault().Enabled;
Assert.IsTrue(state);
现在尝试执行它,它应该按照您的意愿执行。
编辑:正如 Nkosi 在评论中指出的那样,在构造函数中设置了 static Auth 服务来防止这种情况发生。
所以这也需要改变:
public partial class App
{
public static IAuthService AuthApi { get; set; } =new AWSCognito(); // assign it here statically
public App()
{
// AuthApi = new AWSCognito(); <-- remove this
InitializeComponent();
MainPage = new NavigationPage(new LoginPage(AuthApi));
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.