Container has different rules for .net 5 web app and test project - dryioc

I have a small .net 5 WebApi app, using DryIoc. I am now trying to set up an xUnit test suite for this app, but the integration tests fail immediately because one of the registrations has multiple constructors, even though I am using the same rules for both containers and running the same registrations in the same order as the app.
The registration works fine for the app because the container has ConstructorWithResolvableArguments set for the factory method, but it's not being set from anywhere in our code. I know I can easily just add that rule to the container for the tests, but I don't understand why a container set up in the exact same way appears to have different rules, and I am concerned there may be other differences that could affect the tests.
In the app, the container is set up like so:
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new DryIocServiceProviderFactory(Startup.CreateContainer()))
.ConfigureWebHostDefaults(...);
StartUp.cs
...
public static IContainer CreateContainer() => new Container(DiConfiguration.SetRules);
public void ConfigureContainer(IContainer container)
{
container.RegisterCoreDependencies();
// Site specific registrations
container.ConfigureDomain();
}
DiConfiguration.cs
public static class DiConfiguration
{
public static Rules SetRules(Rules rules)
{
rules = rules.WithAutoConcreteTypeResolution();
rules = rules.WithVariantGenericTypesInResolve();
return rules;
}
public static void RegisterCoreDependencies(this IContainer container)
{
// dependency registrations, not much to see here, nothing clever
// just basic registrations e.g.
container.Register<IFoo, Foo>();
}
In the test suite I am setting up the container like so:
public class Test
{
private object _sut;
public Test()
{
var container = new Container(DiConfiguration.SetRules); // <--- Same rules as the app
container.RegisterCoreDependencies(); // <--- fails here
container.ConfigureDomain();
_sut = container.Resolve<Bar>();
}
[Fact]
public void Some_Test_Here()
{
...
}
}
As you can see, all the container registration code is abstracted out into a shared library (the DiConfiguration class). The test is failing calling RegisterCoreDependencies. I don't understand why the rules are different between the two scenarios, is it perhaps something introduced by the DryIocServiceProviderFactory call in the app setting some extra defaults? (DryIocServiceProviderFactory is part of the DryIoc.Microsoft.DependencyInjection package)

The DryIocServiceProviderFactory will override the existing container rules to conform to MS.DI container. Here is the code: https://github.com/dadhi/DryIoc/blob/5e3f1f7edfe237f69ba33c9166d17e284ca4781a/src/DryIoc.Microsoft.DependencyInjection/DryIocAdapter.cs#L97
Here how the rules are overriden in detail:
private static Rules WithMicrosoftDependencyInjectionRules(Rules rules)
{
rules = rules.Clone(cloneMade: true);
rules._settings |= Settings.TrackingDisposableTransients;
rules._settings &= ~Settings.ThrowOnRegisteringDisposableTransient;
rules._settings &= ~Settings.VariantGenericTypesInResolvedCollection;
rules._factorySelector = SelectLastRegisteredFactory;
rules._made._factoryMethod = DryIoc.FactoryMethod.ConstructorWithResolvableArguments;
return rules;
}

Related

ASP.NET Web API Unit Test Autofac Module with BuildManager.GetReferencedAssemblies()

Working on a project in ASP.NET Web API 2 which has Autofac as my IoC container. This project is hosted on IIS and in my Autofac module I use the following method to scan for assemblies:
var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
Why?
https://docs.autofac.org/en/latest/register/scanning.html#iis-hosted-web-applications
But now we are making Unit Tests using NUnit, during my setup I register my module which uses this method. Now I receive the following exception when running my tests:
System.InvalidOperationException: 'This method cannot be called during the application's pre-start initialization phase.'
I understand why I have this exception but I don't have a clue how to make my code work in tests and for deployment environments.
Setup method of NUnit:
[TestFixture]
public abstract class ApplicationTestBase
{
[SetUp]
public override void Init()
{
var builder = new ContainerBuilder();
// If the class requires auto mapper mapping, initialize them
// We do this in order not to init them for every test => optimalisation!
if (GetType().GetCustomAttributes<RequiresAutoMapperMappingsAttribute>(false) != null)
{
builder.RegisterModule<AutoMapperModule>();
}
this.Container = builder.Build();
}
}
Do I need to create a new module specific for my Unit tests or is there another way for this?
AutoMapperTest
[RequiresAutoMapperMappings]
[TestFixture]
public class AutoMapperTests : ApplicationTestBase
{
[Test]
public void Assert_Valid_Mappings()
{
Mapper.AssertConfigurationIsValid();
}
}
UPDATE
Like Cyril mentioned: Why do you need Ioc in your unit tests? I went searching and indeed you don't have to use the Ioc in your tests. So I ditched the Ioc and initialized my mapper configuration byy doing:
Mapper.Initialize(configuration =>
{
var asm = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.StartsWith("ProjectWebService."));
configuration.AddProfiles(asm);
});
I would recommend separating the "how to load assemblies" logic from the "do assembly scanning and register modules logic."
Right now I'm guessing you have something like this all in one method.
public IContainer BuildContainer()
{
var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(asm);
var container = builder.Build();
}
Not exactly that, but something similar - the loading of assemblies is inlined and directly used.
Separate that so you can swap that logic in for testing. For example, consider allowing a parameter to be optionally passed so you can override the logic in test.
public IContainer BuildContainer(Func<IEnumerable<Assembly>> assemblyLoader = null)
{
IEnumerable<Assembly> asm = null;
if (assemblyLoader != null)
{
asm = assemblyLoader();
}
else
{
asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
}
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(asm);
var container = builder.Build();
}
Your default logic will work the way you want, but then in testing you can swap in something else.
var container = BuildContainer(() => AppDomain.GetAssemblies());
There are lots of ways you can do that swap-in. It could be anything from a static property you can set somewhere to a virtual method you can override somewhere. The point is, by separating the assembly loading logic you can get the test-time behavior to work but still use the registration behavior you're after.

How do I manage unit test resources in Kotlin, such as starting/stopping a database connection or an embedded elasticsearch server?

In my Kotlin JUnit tests, I want to start/stop embedded servers and use them within my tests.
I tried using the JUnit #Before annotation on a method in my test class and it works fine, but it isn't the right behaviour since it runs every test case instead of just once.
Therefore I want to use the #BeforeClass annotation on a method, but adding it to a method results in an error saying it must be on a static method. Kotlin doesn't appear to have static methods. And then the same applies for static variables, because I need to keep a reference to the embedded server around for use in the test cases.
So how do I create this embedded database just once for all of my test cases?
class MyTest {
#Before fun setup() {
// works in that it opens the database connection, but is wrong
// since this is per test case instead of being shared for all
}
#BeforeClass fun setupClass() {
// what I want to do instead, but results in error because
// this isn't a static method, and static keyword doesn't exist
}
var referenceToServer: ServerType // wrong because is not static either
...
}
Note: this question is intentionally written and answered by the author (Self-Answered Questions), so that the answers to commonly asked Kotlin topics are present in SO.
Your unit test class usually needs a few things to manage a shared resource for a group of test methods. And in Kotlin you can use #BeforeClass and #AfterClass not in the test class, but rather within its companion object along with the #JvmStatic annotation.
The structure of a test class would look like:
class MyTestClass {
companion object {
init {
// things that may need to be setup before companion class member variables are instantiated
}
// variables you initialize for the class just once:
val someClassVar = initializer()
// variables you initialize for the class later in the #BeforeClass method:
lateinit var someClassLateVar: SomeResource
#BeforeClass #JvmStatic fun setup() {
// things to execute once and keep around for the class
}
#AfterClass #JvmStatic fun teardown() {
// clean up after this class, leave nothing dirty behind
}
}
// variables you initialize per instance of the test class:
val someInstanceVar = initializer()
// variables you initialize per test case later in your #Before methods:
var lateinit someInstanceLateZVar: MyType
#Before fun prepareTest() {
// things to do before each test
}
#After fun cleanupTest() {
// things to do after each test
}
#Test fun testSomething() {
// an actual test case
}
#Test fun testSomethingElse() {
// another test case
}
// ...more test cases
}
Given the above, you should read about:
companion objects - similar to the Class object in Java, but a singleton per class that is not static
#JvmStatic - an annotation that turns a companion object method into a static method on the outer class for Java interop
lateinit - allows a var property to be initialized later when you have a well defined lifecycle
Delegates.notNull() - can be used instead of lateinit for a property that should be set at least once before being read.
Here are fuller examples of test classes for Kotlin that manage embedded resources.
The first is copied and modified from Solr-Undertow tests, and before the test cases are run, configures and starts a Solr-Undertow server. After the tests run, it cleans up any temporary files created by the tests. It also ensures environment variables and system properties are correct before the tests are run. Between test cases it unloads any temporary loaded Solr cores. The test:
class TestServerWithPlugin {
companion object {
val workingDir = Paths.get("test-data/solr-standalone").toAbsolutePath()
val coreWithPluginDir = workingDir.resolve("plugin-test/collection1")
lateinit var server: Server
#BeforeClass #JvmStatic fun setup() {
assertTrue(coreWithPluginDir.exists(), "test core w/plugin does not exist $coreWithPluginDir")
// make sure no system properties are set that could interfere with test
resetEnvProxy()
cleanSysProps()
routeJbossLoggingToSlf4j()
cleanFiles()
val config = mapOf(...)
val configLoader = ServerConfigFromOverridesAndReference(workingDir, config) verifiedBy { loader ->
...
}
assertNotNull(System.getProperty("solr.solr.home"))
server = Server(configLoader)
val (serverStarted, message) = server.run()
if (!serverStarted) {
fail("Server not started: '$message'")
}
}
#AfterClass #JvmStatic fun teardown() {
server.shutdown()
cleanFiles()
resetEnvProxy()
cleanSysProps()
}
private fun cleanSysProps() { ... }
private fun cleanFiles() {
// don't leave any test files behind
coreWithPluginDir.resolve("data").deleteRecursively()
Files.deleteIfExists(coreWithPluginDir.resolve("core.properties"))
Files.deleteIfExists(coreWithPluginDir.resolve("core.properties.unloaded"))
}
}
val adminClient: SolrClient = HttpSolrClient("http://localhost:8983/solr/")
#Before fun prepareTest() {
// anything before each test?
}
#After fun cleanupTest() {
// make sure test cores do not bleed over between test cases
unloadCoreIfExists("tempCollection1")
unloadCoreIfExists("tempCollection2")
unloadCoreIfExists("tempCollection3")
}
private fun unloadCoreIfExists(name: String) { ... }
#Test
fun testServerLoadsPlugin() {
println("Loading core 'withplugin' from dir ${coreWithPluginDir.toString()}")
val response = CoreAdminRequest.createCore("tempCollection1", coreWithPluginDir.toString(), adminClient)
assertEquals(0, response.status)
}
// ... other test cases
}
And another starting AWS DynamoDB local as an embedded database (copied and modified slightly from Running AWS DynamoDB-local embedded). This test must hack the java.library.path before anything else happens or local DynamoDB (using sqlite with binary libraries) won't run. Then it starts a server to share for all test classes, and cleans up temporary data between tests. The test:
class TestAccountManager {
companion object {
init {
// we need to control the "java.library.path" or sqlite cannot find its libraries
val dynLibPath = File("./src/test/dynlib/").absoluteFile
System.setProperty("java.library.path", dynLibPath.toString());
// TEST HACK: if we kill this value in the System classloader, it will be
// recreated on next access allowing java.library.path to be reset
val fieldSysPath = ClassLoader::class.java.getDeclaredField("sys_paths")
fieldSysPath.setAccessible(true)
fieldSysPath.set(null, null)
// ensure logging always goes through Slf4j
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.Slf4jLog")
}
private val localDbPort = 19444
private lateinit var localDb: DynamoDBProxyServer
private lateinit var dbClient: AmazonDynamoDBClient
private lateinit var dynamo: DynamoDB
#BeforeClass #JvmStatic fun setup() {
// do not use ServerRunner, it is evil and doesn't set the port correctly, also
// it resets logging to be off.
localDb = DynamoDBProxyServer(localDbPort, LocalDynamoDBServerHandler(
LocalDynamoDBRequestHandler(0, true, null, true, true), null)
)
localDb.start()
// fake credentials are required even though ignored
val auth = BasicAWSCredentials("fakeKey", "fakeSecret")
dbClient = AmazonDynamoDBClient(auth) initializedWith {
signerRegionOverride = "us-east-1"
setEndpoint("http://localhost:$localDbPort")
}
dynamo = DynamoDB(dbClient)
// create the tables once
AccountManagerSchema.createTables(dbClient)
// for debugging reference
dynamo.listTables().forEach { table ->
println(table.tableName)
}
}
#AfterClass #JvmStatic fun teardown() {
dbClient.shutdown()
localDb.stop()
}
}
val jsonMapper = jacksonObjectMapper()
val dynamoMapper: DynamoDBMapper = DynamoDBMapper(dbClient)
#Before fun prepareTest() {
// insert commonly used test data
setupStaticBillingData(dbClient)
}
#After fun cleanupTest() {
// delete anything that shouldn't survive any test case
deleteAllInTable<Account>()
deleteAllInTable<Organization>()
deleteAllInTable<Billing>()
}
private inline fun <reified T: Any> deleteAllInTable() { ... }
#Test fun testAccountJsonRoundTrip() {
val acct = Account("123", ...)
dynamoMapper.save(acct)
val item = dynamo.getTable("Accounts").getItem("id", "123")
val acctReadJson = jsonMapper.readValue<Account>(item.toJSON())
assertEquals(acct, acctReadJson)
}
// ...more test cases
}
NOTE: some parts of the examples are abbreviated with ...
Managing resources with before/after callbacks in tests, obviously, has it's pros:
Tests are "atomic". A test executes as a whole things with all the callbacks One won't forget to fire up a dependency service before the tests and shut it down after it's done. If done properly, executions callbacks will work on any environment.
Tests are self-contained. There is no external data or setup phases, everything is contained within a few test classes.
It has some cons too. One important of them is that it pollutes the code and makes the code violate single responsibility principle. Tests now not only test something, but perform a heavyweight initialization and resource management. It can be ok in some cases (like configuring an ObjectMapper), but modifying java.library.path or spawning another processes (or in-process embedded databases) are not so innocent.
Why not treat those services as dependencies for your test eligible for "injection", like described by 12factor.net.
This way you start and initialize dependency services somewhere outside of the test code.
Nowadays virtualization and containers are almost everywhere and most developers' machines are able to run Docker. And most of the application have a dockerized version: Elasticsearch, DynamoDB, PostgreSQL and so on. Docker is a perfect solution for external services that your tests need.
It can be a script that runs is run manually by a developer every time she wants to execute tests.
It can be a task run by build tool (e.g. Gradle has awesome dependsOn and finalizedBy DSL for defining dependencies). A task, of course, can execute the same script that developer executes manually using shell-outs / process execs.
It can be a task run by IDE before test execution. Again, it can use the same script.
Most CI / CD providers have a notion of "service" — an external dependency (process) that runs in parallel to your build and can be accessed via it's usual SDK / connector / API: Gitlab, Travis, Bitbucket, AppVeyor, Semaphore, …
This approach:
Frees your test code from initialization logic. Your tests will only test and do nothing more.
Decouples code and data. Adding a new test case can now be done by adding new data into dependency services with it's native toolset. I.e. for SQL databases you'll use SQL, for Amazon DynamoDB you'll use CLI to create tables and put items.
Is closer to a production code, where you obviously do not start those services when your "main" application starts.
Of course, it has it's flaws (basically, the statements I've started from):
Tests are not more "atomic". Dependency service must be started somehow prior test execution. The way it is started may be different in different environments: developer's machine or CI, IDE or build tool CLI.
Tests are not self-contained. Now your seed data may be even packed inside an image, so changing it may require rebuilding a different project.

Sitecore DI with Unity

I'm trying to setup a brand new Sitecore 7.2 website and I'm looking to integrate MVC 5, Glass Mapper and Microsoft Unity as a DI container and Sitecore doesn't want to play very nice.
My scenario is this:
Got a Web project called PoC.Quotes.Web - this will contain only CSS, HTML and any other assets, no controllers
Got a class library project called PoC.Quotes.Controllers - this only contains controllers
Got a class library project called PoC.Quotes.DataLayer - this contain a interface ISitecoreRepository and it's concrete implementation SitecoreRepository
The SitecoreRepository class has a constructor that receives 1 single parameter, the Glass Mapper Context, and one of my controllers receives 1 single parameter in the constructor...the ISitecoreRepository.
Sitecore repository class:
public class SitecoreRepository : ISitecoreRepository
{
ISitecoreContext sitecoreContext = null;
public SitecoreRepository(ISitecoreContext context)
{
this.sitecoreContext = context;
}
}
Controller class:
public class HomeController : Controller
{
private ISitecoreRepository _repository;
public HomeController(ISitecoreRepository repository)
{
this._repository = repository;
}
}
Every time I run the project Sitecore throws an error saying that it cannot create a controller of type (PoC.Quotes.Controllers.HomeController, PoC.Quotes.Controllers). I guess it shows the fully qualified name because that's how I set it up in the controller rendering.
First problem is the controller constructor parameter. I took it out and use this statement to get the instance for the repository:
System.Web.Mvc.DependencyResolver.Current.GetService<ISitecoreRepository>();
The result is null, because the class SitecoreRepository is only having 1 constructor with 1 parameter and it won't get instantiated. Once I get that parameter out of the question too, then all works great.
However, to me this kinda defies the purpose of having a DI container.
I've tried looking at Castle Windsor, but although there is more documentation online, nothing works as I'm getting similar issues.
It is a bit annoying because if I run a similar test in a basic MVC 5 app (I did that just to be sure I'm not going mad), all works fine in less than 5 minutes.
Any ideas?
Edit:
In an interesting twist, after a few good hours spent on this issue, I've noticed that actually either Unity or Windsor containers work fine with one limitation...a big one.
In my controller rendering I've set the controller property as the fully qualified name of the controller:
PoC.Quotes.Controllers.HomeController, PoC.Quotes.Controllers
However, if I go in Sitecore and change that property to just Home, by magic all is good. I've even tried the interim version of using PoC.Quotes.Controllers.Home but still get an error, a different one mind you.
Not sure if I'm doing something wrong but it feels a bit odd.
Any ideas how to get this fixed?
While I can't tell how your registrations are configured, it sounds like you might be better off with a controller factory. The example is Windsor but you could easily swap in Unity. So that you're not modifying the Global.asax, you can also use WebActivatorEx to wire up the bootstrapping startup.
Bootstrapper
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Site.Website.Cms.WindsorConfig), "RegisterComponents")]`
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(Site.Website.Cms.WindsorConfig), "ReleaseComponents")]
/// <summary>
/// Provides a bootstrapping and resolving hook for dependency resolution for MVC, Web API, and service locator.
/// </summary>
public static class WindsorConfig
{
private static Lazy<IWindsorContainer> _container;
static WindsorConfig()
{
_container = new Lazy<IWindsorContainer>(() => BuildContainer());
}
public static IWindsorContainer WindsorContainer
{
get
{
return _container.Value;
}
}
/// <summary>
/// Generates and configures the container when the application is started.
/// </summary>
public static void RegisterComponents()
{
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(WindsorContainer));
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorControllerActivator(WindsorContainer));
}
/// <summary>
/// Disposes of the container when the application is shut down.
/// </summary>
public static void ReleaseComponents()
{
WindsorContainer.Dispose();
}
}
Controller Factory
/// <summary>
/// Provides controller dependency resolving for ASP.NET MVC controllers.
/// </summary>
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IWindsorContainer _container;
public WindsorControllerFactory(IWindsorContainer container)
{
if (container == null) throw new ArgumentNullException("container");
this._container = container;
}
public override void ReleaseController(IController controller)
{
this._container.Release(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)this._container.Resolve(controllerType);
}
}
Spent a considerable amount of time on this and managed to come up with a solution. It was a bit long to write in here so I've put it on a blog post http://agooddayforscience.blogspot.co.uk/2015/10/sitecore-multi-tenancy-and-di-containers.html
Basically it wasn't an issue with Unity or other DI container, it was around how Sitecore handles fully qualified names. Yes, I know that ideally you don't want to use those but to follow the MVC pattern, but I've explained more in the blog post around why use fully qualified names.
As a high level explanation the problem resides in 2 Sitecore classes ControllerRunner and SitecoreControllerFactory. Both classes contain some methods that identify the fully qualified name and use reflection to call the parameter-less constructor to instantiate a new instance. The fix I've applied overrides these methods to call the controller factory regardless.
Thanks for all the help provided.
Andrei

DryIOC Event Aggregator

I'm trying to Implement an event aggregator using DryIOC. I have an Event dispatcher as follows:
public class DryIocEventDispatcher : IEventDispatcher
{
private readonly IContainer _container;
public DryIocEventDispatcher(IContainer container)
{
_container = container;
}
public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : EventArgs
{
foreach (var handler in _container.ResolveMany<IHandles<TEvent>>())
{
handler.Handle(eventToDispatch);
}
}
}
I have a number of classes that can handle events. Indicated by the following Interface:
public interface IHandles<T> where T : System.EventArgs
{
void Handle(T args);
}
The gist of it is, that when I call the event dispatcher dispatch method, and pass in a type that inherits from EventArgs. It grabs from the IOC Container, all the types that Implement IHandles<> and call the handle method on them.
An event type may be handled by multiple Services. And a service can handle multiple event types. e.g:
public class ScoringService : IHandles<ZoneDestroyedEventArgs>, IHandles<ZoneCreatedEventArgs>
{
public void Handle(ZoneDestroyedEventArgs args)
{
Console.WriteLine("Scoring Service Handled ZoneDestroyed Event");
}
public void Handle(ZoneCreatedEventArgs args)
{
Console.WriteLine("Scoring Service Handled ZoneCreated Event");
}
}
public class RenderingService : IHandles<ZoneDestroyedEventArgs>, IHandles<ZoneCreatedEventArgs>
{
public void Handle(ZoneDestroyedEventArgs args)
{
Console.WriteLine("Rendering Service Handled ZoneDestroyed Event");
}
public void Handle(ZoneCreatedEventArgs args)
{
Console.WriteLine("Rendering Service Handled ZoneCreated Event");
}
}
Services need to do other things as well as handle events (but might not have other interfaces as they are not required). Some services need to be a singleton, and the handling of events should respect the singleton registration. Thus a call to container.Resolve(IHandles<>) should return the Singleton type for that service and not make multiple instances. These services are gathering events from multiple sources and therefore need to maintain internal state before sending them off elsewhere. Therefore different eventhandlers calling different services need to be sent to the same underlying instance.
I would like to be able to add IHandles interfaces to any service, and have it picked up automatically without having to fiddle with IOC mappings every time. Ideally service types should be added using convention based mapping as well.
So far I've been working on this for two days. I gave up trying to achieve it with structuremap. Now I'm trying DryIOC - but finding it to be even more difficult to understand and get right.
It is pretty easy to do in DryIoc (I am an owner). Here I will speak about V2 RC version.
Given that you've replaced IContainer dependency with IResolver which is automatically injected:
var container = new Container();
container.Register<IEventDispatcher, DryIocEventDispatcher>();
container.RegisterMany<ScoringService>(Reuse.Singleton);
container.RegisterMany<RenderingService>();
var eventDispatcher = container.Resolve<IEventDispatcher>();
eventDispatcher.Dispatch(new ZoneDestroyedEventArgs());
eventDispatcher.Dispatch(new ZoneCreatedEventArgs());
RegisterMany will take care of handlers being reused as Singleton and will return the same instance for both Handles<> interfaces.
Additionally you may use RegisterMapping to add/map IHandles<> service to already registered implementation.
DryIoc has even more to help with EventAggregator implementation.
Also here is solution for problem similar to yours.
The gist with your working example.

Need help testing Binding in Windows Phone 8

I hope someone has already faced and solved this issue, and can point me to the correct direction.
So I have rest of my unit tests working: Core.Tests has tests for ViewModels to see they are working properly. Now I would like to set up a test project for Phone.Tests that would only test out the binding. So suppose on the login page, something get's entered into the username text box, and that value should be updated in ViewModel and vice-versa.
As a testing framework, I am using WP Toolkit Test framework, and not MS one; WP Toolkit framework runs on the phone itself, meaning it has access to the UI thread.
In theory a test is supposed to look like following:
[TestMethod]
[Asynchronous]
public void Username_Update_View_Should_Update_Model()
{
const string testUsername = "Testing";
var usernameTextBox = GetUiElement<PhoneTextBox>("UsernamePhoneTextBox");
// initial value
Assert.AreEqual(null, _viewModel.Authorization.Username, "Default value should be blank");
//
usernameTextBox.Text = testUsername;
//
Assert.AreEqual(testUsername, _viewModel.Authorization.Username, "Binding not set for {0}", "Username");
}
private T GetUiElement<T>(string name) where T : UIElement
{
return (T)_view.FindName(name);
}
Now, I need to somehow create the view in [TestInitialize] method, and this is what I think I have setup wrong.
I have tried creating the ViewModel manually; then I created the View manually, and binded both DataContext and ViewModel (just to be on safe side) to created viewModel.
At this point, I am expecting changing one property on any one should update the other.
Of-course the error is my test fails. I can't figure out if I should be looking at a custom presenter (all the examples seem to be for ios, droid.) I also tried the following:
public class TestAppStart : MvxNavigatingObject, IMvxAppStart
{
public void Start(object hint = null)
{
ShowViewModel<UserLoginViewModel>();
}
}
and then on my testInitialize I thought I could start it, but I guess I need to find RegisterAppStart and once that's done, try to get the view back from RootFrame.
There must be an easier way... anyone??
Thanks in advance.
Edited: I have got this following as Base Test
public abstract class BaseTest
{
private IMvxIoCProvider _ioc;
protected IMvxIoCProvider Ioc
{
get
{
return _ioc;
}
}
public void Setup()
{
ClearAll();
}
protected virtual void ClearAll()
{
MvxSingleton.ClearAllSingletons();
_ioc = MvxSimpleIoCContainer.Initialize();
_ioc.RegisterSingleton(_ioc);
_ioc.RegisterSingleton((IMvxTrace)new DebugTrace());
InitialiseSingletonCache();
InitialiseMvxSettings();
MvxTrace.Initialize();
AdditionalSetup();
}
private static void InitialiseSingletonCache()
{
MvxSingletonCache.Initialize();
}
protected virtual void InitialiseMvxSettings()
{
_ioc.RegisterSingleton((IMvxSettings)new MvxSettings());
}
protected virtual void AdditionalSetup()
{
_ioc.RegisterSingleton(Mock.Of<ISettings>);
_ioc.RegisterSingleton<IApplicationData>(() => new ApplicationData());
_ioc.RegisterSingleton<IPlatformSpecific>(() => new PlatformSpecific());
_ioc.RegisterSingleton<IValidatorFactory>(() => new ValidatorFactory());
//
_ioc.RegisterType<IMvxMessenger, MvxMessengerHub>();
}
}
On my TestClass initialize, I call base.Setup(), which does setup except the ViewDispatcher. Unfortunately I can't figure out how to use that dispatcher:
I guess really the question I am asking is: how do I get a View through MvvmCross.
PS: I am actually surprised that most don't test the bindings; isn't it where the most amount of mistakes is likely to happen? I am pretty sure the project compiles even if I had bad binding :) scary kind of reminds me of early asp days.
PS: I have actually got another testProject that tests the ViewModels; on that testProject I have managed to hookup following the guidelines at
http://blog.fire-development.com/2013/06/29/mvvmcross-unit-testing-with-autofixture/
Which works beautifully; and also uses autoFixture, NSubstitute and xUnit: and I can't use any of them in Phone Test project.
From my experience, testing the bindings themselves is pretty unusual - most developers stop their testing at the ViewModel and ValueConverter level.
However, if you want to test the bindings, then this should be possible. I suspect the only problem in your current tests is that you haven't initialised any of the MvvmCross infrastructure and so MvxViewModel isn't able to propagate INotifyPropertyChanged events.
If you want to initialise this part of the MvvmCross infrastructure, then be sure to initialise at least:
the MvvmCross IoC container
the MvvmCross main thread dispatcher
This is similar to what is done in the unit tests in the N=29 video - see https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-29-TipCalcTest/TipCalcTest.Tests/FirstViewModelTests.cs#L57
For your app, you can do this using something like:
public static class MiniSetup
{
public static readonly MiniSetup Instance = new MiniSetup();
private MiniSetup()
{
}
public void EnsureInitialized(Context applicationContext)
{
if (MvxSimpleIoCContainer.Instance != null)
return;
var ioc = MvxSimpleIoCContainer.Initialize();
ioc.RegisterSingleton<IMvxTrace>(new MvxDebugOnlyTrace());
MvxTrace.Initialize();
var mockDispatcher = new SimpleDispatcher();
Ioc.RegisterSingleton<IMvxMainThreadDispatcher>(simpleDispatcher);
}
}
where SimpleDispatcher is something like:
public class SimpleDispatcher
: MvxMainThreadDispatcher
{
public readonly List<MvxViewModelRequest> Requests = new List<MvxViewModelRequest>();
public bool RequestMainThreadAction(Action action)
{
action();
return true;
}
}
If you want further MvvmCross functionality available (e.g. ShowViewModel navigation), then you'll need to provide further services - e.g. things like IMvxViewDispatcher - as the number of these increases, you might be better off just running through a full MvxSetup process (like your main app's Setup does)