MvvmCross 5 Unit Test MvxNavigationService - unit-testing

I am updating from MvvmCross 4.4.0 to 5.2.0 and changing to the MvxNavigationService. The Login ViewModel was changed to use the MvxNavigationService and the IMvxNavigationService is registered for both the core and test. The iOS application is functioning correctly. The majority of the unit tests were fixed, but I'm having trouble with verifying the navigation completed correctly using unit test. The trace in the unit test also navigates correctly, but MockDispatcher.Requests is returning null. MockDispatcher.Requests should contain a value after viewModel.OnLoginAsync().Wait(); is called. How do I go about resolving this issue?
The unit test is
[Test]
public void OnLogin_GivenValidCredentials_ThenAppRedirectsToMainPage()
{
Mock<IAuthenticationService> authenticationService = GetAuthenticationService();
SetupCredentialValidity(authenticationService, true);
var viewModel = GetLoginViewModel(null, authenticationService);
viewModel.OnLoginAsync().Wait();
Assert.IsNull(viewModel.ErrorMessage);
Assert.AreEqual(1, MockDispatcher.Requests.Count, "Expected to navigate to the admin view model, but didn't");
Assert.AreEqual(typeof(HomeTabBarViewModel), MockDispatcher.Requests[0].ViewModelType);
}
For the method
public async Task OnLoginAsync()
{
IsActivityIndicatorVisible = true;
try
{
if (Username == MobileAdminUsername && Password == MobileAdminPassword)
{
await _mvxNavigationService.Navigate<AdminViewModel>();
//ShowViewModel<AdminViewModel>();
return;
}
var authenticateResult = await _authenticationService.Authenticate(Username, Password);
LoginResultsVisible = !authenticateResult.Success;
ErrorMessage = authenticateResult.Message;
if (authenticateResult.Success)
{
await _credentialCacheService.SetLastSuccessfulCredentials(Username, Password);
//ShowViewModel<HomeTabBarViewModel>();
await _mvxNavigationService.Navigate<HomeTabBarViewModel>();
}
}
finally
{
IsActivityIndicatorVisible = false;
}
}

Related

Non-overridable members may not be used in setup / verification expressions

I'm trying to do some unittesting on a method that is in a FreshMVVM view model (so no interface).
I want to parse two properties with values as well.
I think I found the way to parse the properties. But I get the following exception while running the tests :
Non-overridable members (here: Search ViewModel.ExecuteSearch Command) may not be used in setup / verification expressions.
The method is set public and so are the properties. I can not change them to virtual because then I get an error in my method.
here is my code:
Viewmodel:
public async void ExecuteSearchCommand()
{
ProductionOrders.Clear();
ObservableCollection<ProductionOrder> allProductionorders = await GetDetailedProductionOrders();
if (SelectedSearch == null || Input== null) {
await Application.Current.MainPage.DisplayAlert("woeps", "please make your selection", "OK");
}
else
{
if (SelectedSearch == "Material")
{
foreach (var productionOrder in allProductionorders)
{
if (productionOrder.MaterialNumber == Input)
{
ProductionOrders.Add(productionOrder);
}
}
}
else
{
foreach (var productionOrder in allProductionorders)
{
if (productionOrder.OrderNumber == int.Parse(Input))
{
ProductionOrders.Add(productionOrder);
}
}
}
if (productionOrders.Count == 0)
{
await Application.Current.MainPage.DisplayAlert("woeps", "No data found for this selection", "OK");
}
}
unit test:
[Fact]
public void ExecuteSearchCommand_WitCorrectData_ListProductionOrders()
{
//Arrange
var testMaterial=testMaterials[0];
var testProductionOrder = testProductionOrders[0];
var mockVm = new Mock<SearchViewModel>();
//act
mockVm.Setup(vm => vm.ExecuteSearchCommand()).Equals(testProductionOrder);
mockVm.SetupProperty(se => se.SelectedSearch,"Production Order") ;
mockVm.SetupProperty(ip => ip.Input, "100001");
Assert.NotNull(mockVm);
}
I also tried this:
[Fact]
public void ExecuteSearchCommand_WitCorrectData_ListProductionOrders()
{
//Arrange
var testMaterial=testMaterials[0];
var testProductionOrder = testProductionOrders[0];
var mockVm = new SearchViewModel { SelectedSearch = "Production Order", Input="100001", ProductionOrders=new ObservableCollection<ProductionOrder>() };
mockVm.ExecuteSearchCommand();
//act
Assert.NotNull(mockVm);
}
But then I get an error in the GetDetailedProductionorders method used in the executesearchcommand()
I don't get this error when running the program (not the unit test)
Could someone give me a hint in the right direction?
Thx!
Sarah
From the second unit test you tried, when you create instance of SearchViewModel, there is no initialize of _productionOrderService.
if _productionOrderService is created in SearchViewModel it might not be initialized due to lack of their dependencies.
you have to provide _productionOrderService to the SearchViewModel by
make it public and set it when create SearchViewModel
make it private and pass it through constructor when create SearchViewModel
then you can mock _productionOrderService and setup GetListAllAsync() in unit test

How to mock UnitOfWork when using transactions (NUnit and NSubstitute)

I have created a Web API using ASP.NET Core 3.1 and EF Core 5.0. Data is stored in a MSSQL database.
I have implemented UnitOfWork and repository pattern.
The UnitOfWork class has a method used to create transactions and it look like this:
//UnitOfWork
public async Task ExecuteTransaction(Func<Task> insideTransactionFunction)
{
var transaction = await _context.Database.BeginTransactionAsync();
try
{
await insideTransactionFunction();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
}
Im trying to write tests, for service classses that uses the UnitOfWork class to modify the data in the SQL database. Fx when adding a user using the UserService:
//UserService
public async Task<User> AddUserAsync(User user)
{
await _unitOfWork.ExecuteTransaction(async () =>
{
//Add user locally to generate the local id
_unitOfWork.UserRepository.Add(user);
await _unitOfWork.SaveChangesAsync();
//...Some logic that uses the generated user id...
if(string.IsNullOrWhiteSpace(email.User) == false)
{
//Inviting the user returns the generated Azure User id
user.AzureUserId = _azureService.InviteUser(user.Email);
await _unitOfWork.SaveChangesAsync();
}
}
return user;
}
A test for the above method could look like this:
//UserServiceTests
public async Task AddUserAsync_UserQualifiesForAzureInvite_AzureUserIdSetOnCreatedUser()
{
//Arrange
var unitOfWork = Substitute.For<IUnitOfWork>();
var azureService = Substitute.For<IAzureService>();
var uut = new UserService(unitOfWork, azureService);
var newUser = new User { ... some setup ... };
var azureUserId = Guid.NewGuid();
azureService.InviteUser(null).ReturnsForAnyArgs(Task.FromResult(azureUserId));
//Act
var createdUser = await _uut.AddUserAsync(newUser); //Returns right away
//Assert
Assert.That(createdUser.AzureUserId, Is.EqualTo(azureUserId));
}
The problem is that the transaction created with UnitOfWork is ignored and the user is returned straight away.
public async Task<User> AddUserAsync(User user)
{
//Skipped from here...
await _unitOfWork.ExecuteTransaction(async () =>
{
//
}
//...To here
return user;
}
So my question is: How can i mock the UnitOfWork class and still be able to test code wrapped in the the transaction?
My architecture was fundamentally flawed. Check out the comments and links made by Panagiotis Kanavos.
The solution was to get rid of UnitOfWork and the repositories and it seems testable now.

How do I perform integration test on WebApi controller using FakeItEasy?

I am new at implementing unit tests and integration tests. I am trying to write some integration tests for my application.
Following are the code snippets from my application to give you all the idea of my code.
It would be great help if you could provide me some guidance for it.
namespace MyApplication.ApiControllers
{
[Authorize]
[RoutePrefix("api/customers")]
[AppExceptionFilter]
public class CustomersController : ApiController
{
private readonly IMediator _mediator;
public CustomersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
[Route("GetCustomer")]
public async Task<IHttpActionResult> GetCustomer(string customerNumber, string customerType = null)
{
var result = await _mediator.RequestAsync(new GetCustomerRequest(customerNumber, customerType));
return Ok(result);
}
}
}
Following is the implementation for GetCustomerRequest handler
public async Task<List<Customer>> HandleAsync(GetCustomerRequest request)
{
var result = await customerService.GetCustomer(request.CustomerNumber, request.CustomerType);
// some business logic
return result;
}
Following is the implementation for customerService
public async Task<List<Customer>> GetCustomer(string customerNumber, string customerType = null)
{
using (var dataContext = _dataContextFactory.Invoke())
{
result = await dataContext.Customers
.Where(b => b.CustomerNumber == customerNumber)
.Where(b => b.CustomerType == customerType)
.Select(b => new Customer
{
// Properties assignment...
})
.ToListAsync();
}
return result;
}
Below is the integration unit test what I have tried.
namespace MyApplication.Tests.Integrations
{
[TestFixture]
public class CustomersControllerTests
{
private string _baseAddress;
private string _username;
private string _password;
private IApiClient _apiClient;
[SetUp]
public void Setup()
{
_baseAddress = "https://mywebaaplication.com"; // TODO get this from a config
_username = "";
_password = "";
_apiClient = new ApiClient(new ApiClientAuthenticationHandler(), _baseAddress); // REPLACE with AzureADApiClientAuthenticationHandler
}
[Test]
public async Task CustomersController_GetCustomer()
{
var customerNumber = string.Empty;
var customerType = 500;
var result = await _apiClient.GetAsync<Customer[]>($"/api/customers/GetCustomer?customerNumber={customerNumber}&customerType={customerType}");
Assert.IsNotNull(result);
Assert.IsTrue(result?.Length > 0);
}
}
}
You can do a few things:
Create a webhost within your unit test, then do http requests against it
Not test your controller in a unit test, but in a liveness/readiness check (because it's just glue code anyway). Just do integration testing for your service.
Just test against "new CustomersController"
There isn't a right/wrong answer here. You just look at the risks, and test accordingly. Also depends on the type of code-changes you expect. Sometimes its fine to create the test only within the context of a new change, no need to anticipate everything.

Testing WebSecurity.Login() problems

I'm trying to test my ASP.NET MVC4 Application within Visual Studio and I am running into problems when testing WebSecurity.Login().
It seems to work perfectly when running my application but throws out an error when testing.
Method to test:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(HomeModels.LoginModel model)
{
if (ModelState.IsValid)
{
try
{
var username = model.Username;
var password = model.Password;
if (WebSecurity.Login(username, password, true))
{
if (Roles.Provider.IsUserInRole(username, "admin"))
{
return RedirectToAction("AdminHome");
}
else if (Roles.Provider.IsUserInRole(username, "user"))
{
return RedirectToAction("LoginSuccessful");
}
}
else
{
//String errorMessage = "Login was not successful.";
}
}
catch (MemberAccessException e)
{
ModelState.AddModelError("", e);
}
}
return View(model);
}
Test Method:
[TestMethod]
public void TestLoginAdminSuccessfulView()
{
HomeController controller = new HomeController();
Ecommerce.Models.HomeModels.LoginModel login = new Ecommerce.Models.HomeModels.LoginModel();
login.Username = "sgupta";
login.Password = "sgupta2189";
var result = (RedirectToRouteResult) controller.Login(login);
Assert.AreEqual("AdminHome", result.RouteName);
}
Error Message:
Test method EcommerceUnitTests.Controllers.HomeControllerTest.TestLoginAdminSuccessfulView threw exception:
System.InvalidOperationException: To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider".
Error Stack Trace:
WebMatrix.WebData.WebSecurity.VerifyProvider()
WebMatrix.WebData.WebSecurity.Login(String userName, String password, Boolean persistCookie)
Ecommerce.Controllers.HomeController.Login(LoginModel model)
EcommerceUnitTests.Controllers.HomeControllerTest.TestLoginAdminSuccessfulView()
copy the working web.config to app.config in the project containing your tests.
Make sure your Test project references assembly that is / contains your custom member provider.
If the main project is working, check what references it has.
I ended up restructuring it all and using ASP.NET's default membership provider.

Unit Testing MVC 4 RedirectToAction

I am trying to unit test the redirection of my controller in MVC 4 .Net 4.5. Here is an example:
[TestMethod]
public void Register_PassValidModel_RedirectToHomeIndexShouldBeTrue()
{
//Arrange
var registerModel = new RegisterModel
{
Email = "validEmailAddress#domain.com",
Password = "password"
};
//Assign
var result = _controller.Register(registerModel) as RedirectToRouteResult;
//Assert
result.RouteValues["Action"].ShouldBeEqual("Index");
result.RouteValues["Controller"].ShouldBeEqual("Home");
}
Here is the controller:
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
var userToRegister = new User { Email = model.Email, Password = model.Password };
var service = new UserService(_userRepository);
User user = service.RegisterUser(userToRegister);
if (user.UserErrorMessages.Count != 0)
{
user.UserErrorMessages.ForEach(x => ModelState.AddModelError("", x));
return View(model);
}
SetCookie(model.Email);
return RedirectToAction("Index", "Home");
}
return View(model);
}
The issue the variable result in the Unit Test is null. I found this code from someone who was working on a MVC 2 project and it seemed to work for him. Has something changed with MVC 4?
Thanks in advance!
Try this one hope it will useful for you
var result= (RedirectToRouteResult)controller.Register(registrModel);
result.RouteValues["action"].Equals("Index");
result.RouteValues["controller"].Equals("Home");
Assert.AreEqual("Index", action.RouteValues["action"]);
Assert.AreEqual("Home", action.RouteValues["controller"]);