Mock and test MvvmCross NavigationService - xamarin

I am using the navigation service to perform the navigation between ViewModels and now I have to create some unit tests for my ViewModel. I am able to mock the ViewModel creating the injected objects with Moq but I am struggling to mock NavigationService and then create the assertion than it can navigate to the next ViewModel.
I have found some documentation from a couple of years ago so the navigation service did not exist and I cannot find anything about this matter at official MvvmCross documentation so right now I am in a dead end.
I am using MvvmCross 5.5.0.
[Test]
public void CheckStatus()
{
Mock<IStatusService> _mockStatusService = new Mock<IStatusService>();
_mockStatusService.Setup(x => x.Check(It.IsAny<StatusRequest>())).Returns(() => new StatusRequest { Ok = true });
//Here I need to pass the NavigationService to the constructor
var _viewModel = new StatusViewModel(_mockStatusService.Object, navigationService);
_viewModel.Start();
//Here I guess I should perform the navigation assertion.
}

I have found how to mock and test the NavigationService. I post here the solution because it would be useful for someone else.
[Test]
public void CheckStatus()
{
IFixture _fixture = new Fixture().Customize(new AutoMoqCustomization());
Mock<IMvxNavigationService> _navigationService = _fixture.Freeze<Mock<IMvxNavigationService>>();
var _viewModel = new StatusViewModel(navigationService);
_viewModel.Start();
_navigationService.Verify(x => x.Navigate<NextViewModel>(null), Times.Once());
}

Related

testing navigation from one view model to other viewmodel

I am trying to create test which be able to check navigation between two viewmodels.
Some code from ResultViewModel
private IMvxCommand homeCommand;
public IMvxCommand HomeCommand => homeCommand ?? (homeCommand = new MvxAsyncCommand(ShowHomePage));
private async Task ShowHomePage()
{
await _navigationService.Navigate<HomeViewModel>();
}
Testing code
private Mock mvxNavigationService = new Mock();
private Mock resultService = new Mock();
[Test]
public void Test1()
{
var viewModel = new ResultViewModel(mvxNavigationService.Object, resultService.Object);
viewModel.HomeCommand.Execute();
mvxNavigationService.Verify(service => service.Navigate<HomeViewModel>());
Assert.Pass();
}
I am getting error
"An expression tree cannot contain a call or invocation that uses optional arguments."
on line
mvxNavigationService.Verify(service => service.Navigate());
Not sure where the issue is.
In Xamarin, a dedicated page navigation method is officially provided.
You can navigate between pages like:
await Navigation.PushAsync (new Page());
You can pop pages from the navigation stack like:
await Navigation.PopAsync ();
For more usage, please refer to: Hierarchical Navigation.

Triggering event in Activity using MvvmCross

I have an MvxFragmentActivity which loads a google map and places markers on the map. The code to create the map and markers is very Droid specific so it is in the Activity. The markers are created based on objects in the ViewModel which each contain lat/long coordinates. This worked fine as long as I loaded the objects in my Init method. I have since moved the load objects method to a service and call it on a different thread. This way the UI is responsive. However, how do I call the method in the Activity when the load is completed?
Here is my current code in the Activity (this code shouldn't change, just how it is called):
private void InitMapFragment()
{
foreach (var item in viewModel.Items)
{
var icon = BitmapDescriptorFactory.FromResource(Resource.Drawable.place_img);
var markerOptions = new MarkerOptions()
.SetPosition(new LatLng(item.Latitude, item.Longitude))
.InvokeIcon(icon)
.SetSnippet(item.DistanceText)
.SetTitle(item.Name);
var marker = _map.AddMarker(markerOptions);
_markerIds.Add(marker.Id, item.Id);
}
}
Code in my viewModel:
private void BeginLoadItems()
{
_loadItemsService.Load();
}
// This is triggered by a message
private void OnLoadItemsComplete(LoadCompleteMessage message)
{
Items = message.Items;
}
Code in my Service:
public void Load()
{
ThreadPool.QueueUserWorkItem(state =>
{
var results = _repository.Retrieve();
_messenger.Publish(new LoadCompleteMessage(this, results));
});
}
You're already triggering an event when you set:
Items = message.Items;
This triggers PropertyChanged with a property name of "Items"
For more on map binding, see Using MvvmCross how can I bind a list of annotations to a MapView? - although with Droid you'll need to use markers instead of annotations.

Add Aspects to ASP.NET MVC controller using AspectMap

We're looking at using an AOP framework for handling things like logging, tracing, and exception handling. I've built a prototype using PostSharp and now I'm trying to build the same functionality using AspectMap.
In a nutshell, I have an ASP.NET MVC 3 application and I want an aspect that I can easily attach to my controller methods that shows the entry, exit, execution time, and argument values. My PoC is the basic MVC 3 Internet Application template (File > New > Project > Web > ASP.NET MVC 3 Web Application > Internet). What I've done so far...
Created an AspectsRegistry
public class PoCRegistry : AspectsRegistry
{
public PoCRegistry()
{
ForAspect<ProfileAttribute>().HandleWith<ProfileHandler>();
}
}
Created a StructureMapControllerFactory
public class StuctureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType )
{
if( controllerType == null ) return null;
try
{
return ObjectFactory.GetInstance( controllerType ) as Controller;
}
catch( StructureMapException )
{
Debug.WriteLine( ObjectFactory.WhatDoIHave() );
throw;
}
}
}
Registered everything in Application_Start
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters( GlobalFilters.Filters );
RegisterRoutes( RouteTable.Routes );
ObjectFactory.Initialize( ie => ie.AddRegistry( new PoCRegistry() ) );
ControllerBuilder.Current.SetControllerFactory( new StuctureMapControllerFactory() );
}
At this point the application works, and I can see it's using my StructureMapControllerFactory to build the controller (debugger steps into that code). The problem is that I can't figure out where or how to "enrich" the controller that is generated. In the tutorial it says I need to use something like the following:
For<ICaseController>()
.Use<CaseController>()
.EnrichWith( AddAspectsTo<CaseController> );
But in the tutorial that goes in the AspectRegistry, which doesn't seem like the right place in this situation because the registry isn't responsible for resolving the controller request, the controller factory is. Unfortunately the GetInstance() method in the controller factory returns an object and the EnrichWith() method needs a SmartInstance.
At this point I'm stuck. Any hints, pointers, or assistance would be appreciated.
This is a use case I hadn't thought about to be honest. I'll setup a test project today and see what I can come up with. Bear with me!
Update
I've been playing around with the backend code (you can get a complete copy of the code from http://aspectmap.codeplex.com) and the relevant part is this:
public T AddAspectsTo<T>(T concreteObject)
{
ProxyGenerator dynamicProxy = new ProxyGenerator();
return (T)dynamicProxy.CreateInterfaceProxyWithTargetInterface(typeof(T), concreteObject,
new[] { (IInterceptor)new AspectInterceptor(attributeMap) });
}
This is using the castle dynamic proxy stuff. Unfortunately the CreateInterfaceProxy... methods require that an interface is passed in (rather than a base class like I'd hoped). Now I've found this question:
C# Dynamic Proxy 2 generate proxy from class with code in constructor ? How to?
That seems to show that it could be possible to use CreateClassProxy. I've not had chance to try this out yet and I'm going away for a week away from the computer. If you want to try and wire it up though you're welcome to get the source from codeplex and give it a try though. If not I'll put something together when I return.
Action filters can be used to provide such AOP functionality.
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
http://msdn.microsoft.com/en-us/library/dd410056%28v=vs.90%29.aspx

How To: Caliburn.Micro.Autofac and Windows Phone

Is there and example, tutorial or anything that shows how to use Caliburn.Micro.Autofac with Windows Phone?
I created a basic application with Caliburn.Micro only, and that runs fine. Then I decided to use Caliburn.Micro.Autofac, so I derived my Bootstrapper from Caliburn.Micro.Autofac.AutofacBootstrapper and called base.Configure() inside the Bootstrapper Configure() method. Now wen I ran the application I get "The type 'AppBootstrapper' was not found." exception.
Appreciate any help.
This is the bootstrapper I wrote for a WP7 project. It's based on Caliburn.Micro.Autofac.AutofacBootstrapper but fixes some bugs.
public class AppBootstrapper : PhoneBootstrapper
{
private IContainer container;
protected void ConfigureContainer(ContainerBuilder builder)
{
// put any custom bindings here
}
#region Standard Autofac/Caliburn.Micro Bootstrapper
protected override void Configure()
{
// configure container
var builder = new ContainerBuilder();
// register phone services
var caliburnAssembly = AssemblySource.Instance.Union(new[] { typeof(IStorageMechanism).Assembly }).ToArray();
// register IStorageMechanism implementors
builder.RegisterAssemblyTypes(caliburnAssembly)
.Where(type => typeof(IStorageMechanism).IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface)
.As<IStorageMechanism>()
.SingleInstance();
// register IStorageHandler implementors
builder.RegisterAssemblyTypes(caliburnAssembly)
.Where(type => typeof(IStorageHandler).IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface)
.As<IStorageHandler>()
.SingleInstance();
// The constructor of these services must be called
// to attach to the framework properly.
var phoneService = new PhoneApplicationServiceAdapter(RootFrame);
var navigationService = new FrameAdapter(RootFrame, false);
builder.Register<IPhoneContainer>(c => new AutofacPhoneContainer(c)).SingleInstance();
builder.RegisterInstance<INavigationService>(navigationService).SingleInstance();
builder.RegisterInstance<IPhoneService>(phoneService).SingleInstance();
builder.Register<IEventAggregator>(c => new EventAggregator()).SingleInstance();
builder.Register<IWindowManager>(c => new WindowManager()).SingleInstance();
builder.Register<IVibrateController>(c => new SystemVibrateController()).SingleInstance();
builder.Register<ISoundEffectPlayer>(c => new XnaSoundEffectPlayer()).SingleInstance();
builder.RegisterType<StorageCoordinator>().AsSelf().SingleInstance();
builder.RegisterType<TaskController>().AsSelf().SingleInstance();
// allow derived classes to add to the container
ConfigureContainer(builder);
// build the container
container = builder.Build();
// start services
container.Resolve<StorageCoordinator>().Start();
container.Resolve<TaskController>().Start();
// add custom conventions for the phone
AddCustomConventions();
}
protected override object GetInstance(Type service, string key)
{
object instance;
if (string.IsNullOrEmpty(key))
{
if (container.TryResolve(service, out instance))
return instance;
}
else
{
if (container.TryResolveNamed(key, service, out instance))
return instance;
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", key ?? service.Name));
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.Resolve(typeof(IEnumerable<>).MakeGenericType(service)) as IEnumerable<object>;
}
protected override void BuildUp(object instance)
{
container.InjectProperties(instance);
}
private static void AddCustomConventions()
{
ConventionManager.AddElementConvention<Pivot>(Pivot.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Pivot.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Pivot.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
ConventionManager.AddElementConvention<Panorama>(Panorama.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Panorama.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Panorama.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
}
#endregion
}
EDIT I have created a fork of Caliburn.Micro.Autofac and fixed the issue on GitHub. Hopefully the pull request will be accepted and this will become part of the main repository.
For now, you can access the bootstrapper, and AutofacPhoneContainer from here - https://github.com/distantcam/Caliburn.Micro.Autofac/tree/master/src/Caliburn.Micro.Autofac-WP7
I have implemented a proper version (in my opinion) of Caliburn.Micro.Autofac for Windows Phone. You can download it and test project from my blog. The blog post is in Russian but you'll find the link to ZIP file in the top of the post. The code is too big to post here, so please take from the blog. I've send this to David Buksbaum (the author of Caliburn.Micro.Autofac). Hope he will incorporate it into his code base soon.
UPDATE
What is fixed:
Components realizing IPhoneService and INavigationService services must be instantiated before registering in container.
Realized component implementing IPhoneContainer. Without it you can't use Autofac in Caliburn.Micro.

How to mock up a static method in a static class with URLHelp? (Moq)

I have a extension method. Can any one help me how to test this method with Moq?
public static string GetBaseUrl(this UrlHelper urlHelper)
{
Uri contextUri = new Uri(urlHelper.RequestContext.HttpContext.Request.Url, urlHelper.RequestContext.HttpContext.Request.RawUrl);
UriBuilder realmUri = new UriBuilder(contextUri) { Path = urlHelper.RequestContext.HttpContext.Request.ApplicationPath, Query = null, Fragment = null };
string url = realmUri.Uri.AbsoluteUri;
if (url.EndsWith("/"))
{
url = url.Remove(url.Length - 1, 1);
}
return url;
}
many thanks.
As TrueWill points out, you can't use Moq directly with UrlHelper.RequestContext because it isn't virtual. On the other hand, UrlHelper is a public class that you can instantiate for use with unit testing.
At some point, however, you will encounter the need to assign a HttpContextBase to create the UrlHelper, and Moq can help you to do that.
Here's a test that shows that I can at least write a unit test that invokes your GetBaseUrl without throwing any exceptions:
[TestMethod]
public void Test1()
{
var httpCtxStub = new Mock<HttpContextBase>();
httpCtxStub.SetupGet(x => x.Request).Returns(() =>
{
var reqStub = new Mock<HttpRequestBase>();
reqStub.SetupGet(r => r.RawUrl).Returns("http://foo");
reqStub.SetupGet(r => r.Url).Returns(new Uri("http://foo"));
return reqStub.Object;
});
var requestCtx = new RequestContext(httpCtxStub.Object, new RouteData());
var urlHelper = new UrlHelper(requestCtx, new RouteCollection());
var result = urlHelper.GetBaseUrl();
// Assert something
}
However, this isn't the simplest unit test to write and maintain, so I support TrueWill's comment that you might make life simpler for yourself if you hide UrlHelper behind an interface.
The UrlHelper.RequestContext property is non-virtual. Moq isn't going to be of help in this case, to the best of my knowledge.
You could create a wrapper class for UrlHelper that implements an interface, but that would seem to defeat the purpose of using an extension method.
Typemock would probably do what you want, if you have the budget for a commercial program. (I haven't tried it; I use Moq myself.)
Another option would be to write integration tests against this method; while they would run more slowly than unit tests, I suspect this method is unlikely to version often.
A larger issue is coupling to UrlHelper reducing testability in the rest of your application. Perhaps other posters can suggest answers to that issue.

Resources