Win10 UWP, Prism/Unity and Unit Tests plus Class Libraries, silent failure - c#

Basic layout is currently a simple app. Nothing fancy, I've got Views in the App.Views namespace and my ViewModels in the App.ViewModels namespace. The ViewModels are autowired to the Views through the XAML directive:
xmlns:prismMvvm="using:Prism.Windows.Mvvm"
prismMvvm:ViewModelLocator.AutoWireViewModel="True"
So, basically, this works. Then I wanted to leverage Unity's IoC / DependencyInjection for some unit tests.
The usual way would be to simply add a Windows 10 Unit Test App to the existing app and reference the latter in the Unit Test App.
This will crash upon executing the unit tests because it seems that you may not derive the App class from anything other but Application.
I.e. this works:
public sealed partial class App : Application
This does not:
public sealed partial class App : PrismUnityApplication
It's probably also not Prism's fault and something Microsoft has to fix on their end.
Now, the proposed workaround is to simply put whatever you want to unit test into a class library and reference this library from the unit test app. This works for unit testing. It also works for the Models.
My naive approach does not work, however, for the ViewModels. The ViewModel classes are still found under the App.ViewModels namespace, as before, just that they're now in a Class Library. I can access them programmatically in the main app just fine. But upon running the program, the AutoWiring silently fails without an error.
Even when I do something like this, it still does not work:
ViewModelLocationProvider.Register(typeof(MainPage).ToString(), () => new ViewModels.MainPageViewModel());
I'm not that experienced with the technologies involved yet so without an actual error I'm a bit at a loss.
edit: Just to add to the mystery - this code does work, regardless of whether the ViewModel resides in the main app or the class library:
var x = Container.Resolve(typeof(ExamplePageViewModel)) as ExamplePageViewModel;

You are correct, this is a limitation of UWP application testability in general. The testability of UWP apps is imperfect right now. In order to fix the first issue, you need to add the BindableAttribute to your application class:
[Bindable]
sealed partial class App : PrismUnityApplication
{
}
As far as pulling your Views/ViewModels out into separate assemblies, this issue is due to how UWP handles loading types form separate assemblies. None the less, this shouldn't stop you from testing your ViewModels. You can use a regular class library to test your ViewModel logic. You will mock your dependencies. You shouldn't be creating instances of your Views to test your ViewModels. So the ViewModelLocator becomes a non-issue.

We got the same issue as you, test silently failing.
when we looked at the Test output window we saw this:
App activation failed.
Failed to initialize client proxy: could not connect to test process.
Inside our [UnitTestApp], which inherits [PrismUnityApplication], we override [ConfigureContainer] method, and setup mocks on the container, instead of setting up real classes, i.e.
UnitTestApp.Current.Container.RegisterInstance(myMock.Object, new ContainerControlledLifetimeManager());
You may also have the Container setup in each test class' constructor
This way we got our test running without a silent failure.
It seemed that the silent failure was due to the container of the actual App class being called in the context of the UnitTestApp class - but I cannot confirm 100%

Related

Creating Unit Tests in VS only allowed in Public Classes?

I am trying to clarify this notification message when trying to create a unit test for my method. The message clearly states that creating a unit test is only supported on a NON-Test project and within a public class OR a public method. I clearly have a public method but the class isn't. So is this just an incorrectly typed error message? Does it actually mean that you need to have both a public class and method?
Note: It works when I try this in a public class, just testing the notification.
Picture of notification message:
To test your class/method, you have to create another project - Testing project. Look at this as it has been another application that uses your classes.
So for example you have 3 projects (for sake of simplicity):
Domain - project with models and domain services
Console application - application that uses domain project
Test... let's say "Testing application" that tests Domain project.
So it points that class that you want to test MUST be available from test project. So it has to be public. You can also use attribute InternalsVisibleTo to make this class available for test project.
Your unit test project must be able to see (public visibility) the method you want to test in order to call it and execute it.
Here is a sample on a WinForm application:
You can clone the whole solution here:
Visual Studio solution on GitHub
Note that you can unit test private methods but it is not a good practice:
Solution downloadable here:
visual Studio solution on GitHub

Inheriting test methods across projects

I'm trying to have a test class for the VS test framework, that is derived from another test class and inherits all its test methods (so that each appears twice). This was suggested in a comment on another ticket.
When this is done within the same project, it works fine. When done across projects, everything compiles fine but the new tests don't show up.
In this minimal solution, the problem is demonstrated. Two projects, in each we derive from the same base class in the first project. But the new (duplicate) tests only show up for the same-project child class.
I suspect something is optimized away in someway, but don't know what or how top debug this. I tried adding a dummy TestMethod to the second-project test class, just so that something must happen with the class - then only this test method shows, without the derived methods. Suggestions would be appreciated.

Ninject fails to bind when running from NUnit

i have come across a problem i am not sure how to best solve, ideally without redoing the code.
Prologue:
I was handed an existing application to maintain and as necessary, upgrade. It's an C# application. Now, i am not a pro when it comes to c#, but i am good enough to get things done while not making anyones eyes bleed when reading my code.
The problem is, the application uses NUnit (not that big of a problem, i have experience with that and i understand the basics) and Ninject (thats a different story).
Issue:
The application uses Ninject to bind several classes to the kernel. My goal was to modify the application a bit to allow support for some different objects.
Now, when i debug the application or i build it and deploy, it works.
However, running Nunit, i get an Ninject.ActivationException: Error activating IDatabaseConnection exception. (IdatabaseCnnection is an interface from internal library.
I tried to recreate the test that fails in an console application and it works there as well, it fails only when run through NUnit
Ninject.ActivationException: Error activating IDatabaseConnection
No matching bindings are available, and the type is not self-bindable.
Activation path:
3) Injection of dependency IDatabaseConnection into parameter databaseConnection of constructor of type OrderMailDefinitionSource
2) Injection of dependency IMailDefinitionSource into parameter mailDefinitionSource of constructor of type DocumentMailService
1) Request for DocumentMailService
Suggestions:
1) Ensure that you have defined a binding for IDatabaseConnection.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
So as far as i can tell, for some reason when running through NUnit, the binding fails. Running it in any way just not through NUnit, it works.
If you would have any ideas, i would be thankfull.
Have a nice day.
Answer
This was the problem (originally didnt use the CodeBase property). Nunit didnt copy the DLLs and so failed to load about 21 different dlls. After this was fixxed it worked.
kernel.Bind(x => x.FromAssembliesInPath(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath))
.SelectAllClasses()
.BindDefaultInterface());

Error activating IInterceptor... only through COM?

TL;DR: Kernel.Get<T> works when called from a .net/WPF app, but blows up with an inner ActivationException (inside a TargetInvocationException) when called from a VB6 app. WTH?
This is a bit of a follow-up on this question where I configured an abstract factory with Ninject conventions, in such a way that I never need to actually implement one, the Ninject factory extension takes care of generating one on-the-fly.
This worked beautifully... until I needed to run my library from VB6 code.
_kernel.Bind(t => t.FromAssemblyContaining(typeof(ViewModelBase))
.SelectAllInterfaces()
.EndingWith("ViewFactory")
.BindToFactory());
As soon as I call anything on the app from VB6 code, if I wrap the resolving of dependencies inside a try/catch block, I'm trapping a TargetInvocationException with an inner ActivationException:
Error activating IInterceptor using conditional implicit self-binding
of IInterceptor Provider returned null. Activation path:
3) Injection of dependency IInterceptor into parameter of constructor of type IViewFactoryProxy
2) Injection of dependency IViewFactory into parameter viewFactory of constructor of type MsgBox
1) Request for IMsgBox
Suggestions:
1) Ensure that the provider handles creation requests properly.
I have no reference to the Ninject.Interception extension (at this point).
Oddly if instead of launching VB6 I launch a sandbox WPF test app when I debug, I don't get any ActivationException and everything works like a charm.
The VB6 code dies with automation error -2146232828 (80131604) which yields nothing on Google, but I'm guessing it has to do with the TargetInvocationException being thrown.
As far as .net code is concerned it just works: if I compose the app from a WPF client I can break in the MsgBox class constructor and see that the IViewFactory parameter is happy with a Castle.Proxy.IViewFactoryProxy; if I compose the app from a VB6 ActiveX DLL (I also created a VB6 EXE to test and same as the DLL), it blows up.
UPDATE
I removed the generic abstract factories, and I no longer get this error. And because I don't want to be writing factories, I went for a bit of tighter coupling that I can live with. Now I'd like to know why this was happening!
I ran into this exception today in a completely different context to you. I was trying to use a kernel configured with a custom module inside a design time view model in the VS WPF Designer. The module had a number of interfaces configured using the ToFactory() extension method.
The problem was that for some reason the Ninject.Extensions.Factory.FuncModule was not loaded automatically when I was initialising my kernel, possibly due to some trickery in the way the VS designer handles creating design time classes (maybe it didn't load the appropriate assembly or something, who knows).
I had a unit test that was creating one of these design time view models, and it worked perfectly, so it was definitely something related to the designer.
I fixed the issue by creating a special kernel for my design time view models.
public class DT_Kernel : StandardKernel
{
public DT_Kernel()
: base(new MyModule())
{
if (!HasModule(typeof(FuncModule).FullName))
{
Load(new[] { new FuncModule() });
}
}
}
The important part of this code is the bit that loads the FuncModule if it isn't already loaded.
You might be able to leverage that code to fix your issue.

Testing Custom Bootstrappers in Nancy

So I have a CustomBootstrapper, which does a lot of application initialization, including IoC registration and Quartz scheduler setup. The modules also heavily rely on the SuperSimpleViewEngine.
Now I need to test this, using MSTest of course, and as everyone has probably figured out by now this is not going to work. The Nancy guys figured this out early, and have provided this workaround : https://github.com/NancyFx/Nancy/wiki/Nancy-Testing-View-Location which I assume works, because when I in my test case try to create an instance of my bootstrapper it fails miserably, because there are more than one RootPathProviders.
So the solution, it would seem is to use a ConfigurableBootStrapper. So I guess the only question is, how do I make sure that the ConfigurableBootStrapper is setup the same way as my CustomBootStraper?
var browser = new Browser(cfg =>
{
cfg.RootPathProvider<YourRootPathProvider>();
cfg.Module<YourModule>();
});
In your tests, create a test bootstrapper, which inherits from your custom one, then override only the functionality which breaks. Use that test bootstrapper for tests, assuming that your overrides are minimal.

Categories

Resources