Nunit unit test for WPF application gives NullReference exception - wpf

While i was writing unit test(NUnit) for wpf application. Got an error 'object reference not set to an instance'
at the bottom level i can see the line which raise exception
Application.Current.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() => loc.ResolveLocalizedValue(out ui)));
this code is used in helper class library.
Thanks in advance. Guys please suggest how can i resolve this.

Application.Current will be null in your case. It returns application object for the current AppDomain which gets set only when application is run.
Test cases are to test business logic i.e. to test ViewModel layer. Code which you want to test should not contain any reference to UI component.
More details can be found here - Application.Current is null in unit test cases. Workaround is provided in the link but I strongly discourage the use of Application.Current in your testcases or usage of it in ViewModel layer which needs to be unit tested.

Related

Accessing a grid control with UIAutomation in .NET 5

We've got a C# GUI application (WPF) that for various reasons uses .NET Core 3.1. In that application we have a custom grid control that we would like to talk to using UIAutomation.
Our test harness application, a console app using .NET 5.0, does this:
Uses Process.Start() to run our main application.
Creates an instance of CUIAutomation.
Passes the handle of the main application's main window to IUIAutomation.ElementFromHandle() to get an IUIAutomationElement object.
Calls FindAll(TreeScope.TreeScope_Children) recursively to find all the elements. Those elements that have an automation ID are stored in a dictionary.
That all works great. In the test harness, we can load the main application, and then find the element with automation ID "ExitButton" and click on it. Woohoo!
So back in the main application, we have a grid. We assign the grid an automation ID, and when we access that element in the test harness, we can see the grid's OnCreateAutomationPeer() being called. It returns a custom automation peer object (derives from FrameworkElementAutomationPeer, also implements IGridProvider), and our peer object responds to a few basic requests (IsControlElementCore, GetAutomationIdCore) so we know the test harness & peer are talking.
So now the challenge.
How do we get our test harness to do any grid stuff?
From reading online, it seems like we need to call something like
gridElement.GetCurrentPropertyValue(GridPattern.ColumnCountProperty)
but no matter what we try, we cannot find anything named "GridPattern". It doesn't show up in any of the Windows UIAutomation DLLs, nothing in Nuget seems to help.
Where is GridPattern defined? Or are we getting at the grid testing functionality the wrong way?
EDIT #1
By manually editing our test harness' CSPROJ file and adding a reference to Microsoft.WindowsDesktop.App.WPF (as described in this question) the compiler now knows what GridPattern is. However, we're still not sure how to access grid functionality in our test harness.
EDIT #2
The compiler lets us call
GetCurrentPattern(GridPatternIdentifiers.ColumnCountProperty.Id)
in our test harness, but at runtime it throws an exception "Value does not fall within the expected range."
EDIT #3
Calling GetCurrentPropertyValue(GridPatternIdentifiers.ColumnCountProperty.Id) in the test harness works correctly to retrieve the value of the ColumnCount property on the peer object in the main application
EDIT #4
Calling GetCurrentPattern(GridPatternIdentifiers.Pattern.Id) in our test harness doesn't throw an exception, but returns an object of type System.__ComObject which is not obviously useful. Simply casting the __ComObject to a GridPattern throws the expected exception "Cannot convert type 'System.__ComObject' to 'System.Windows.Automation.GridPattern'"
EDIT #5
From looking through the source code, we discovered the GUID of UIAutomationClient.IUIAutomationGridPattern. Passing this to GetCurrentPatternAs(GridPatternIdentifiers.Pattern.Id, gridPatternGUID) works, in that it doesn't fail, but rather returns the IntPtr value 1526502600256.
Only remaining question:
There is a function GridPattern.GetItem() used to retrieve an automation element the represents a specific cell. We still don't know how to get an actual GridPattern object in the test harness.
Finally found the last piece of the magic incantation:
UIAutomationClient.IUIAutomationGridPattern gridPattern = transmitterGrid.Auto.GetCurrentPattern(GridPatternIdentifiers.Pattern.Id) as UIAutomationClient.IUIAutomationGridPattern;
This gives me a gridPattern object that, when called from the test harness, invokes the matching function in the peer in the main application.

How do I implement IActivationForViewFetcher for a child UserControl?

I've just added ReactiveUI to an existing code base. Of course, for the first control I tried it with I hit a snag. I'm using it with a UserControl embedded in a TabControl. The code looks something like this:
public partial class TabPageControl : UserControl, IViewFor<TestViewModel>
{
public TabPageControl()
{
InitializeComponent();
ViewModel = new TestViewModel();
this.WhenActivated(dispose =>
{
dispose(this.Bind( ... ));
dispose(this.BindCommand( ... ));
});
}
}
When I run the app, I get the following error message:
Don't know how to detect when TabPageControl
is activated/deactivated, you may need to implement
IActivationForViewFetcher
So, how do I implement IActivationForViewFetcher? I'm not sure what I'm supposed to do with GetAffinityForView. I'm assuming in GetActivationForView I need to check to see if the UserControl is the currently visible inside the TabControl?
Although I would like to understand how to implement the methods for IActivationForViewFetcher (especially the part where I identify that a control is in the VisualTree) - the real cause of my problem was that my main assembly didn't have the appropriate references (the controls are in a class assembly).
I'm assuming (because I've skimmed the ReactiveUI source) ReactiveUI.Winforms.Registrations needs to be instantiated by the main assembly - which includes registering ActivationForViewFetcher.
Incidentally, the class library is written in C# and the main assembly is VB.NET. So I'm not sure whether this contributed to the problem.
At least it's working now!
I don't if this will ever help anybody, since this thread is so old.
What solved my issue was having ReactiveUI.WPF,ReactiveUI.WinForms, CefSharp.WPF and CefSharp.WinForms NuGet references on all the projects/plugins that were running on the App.
My suspicion is that when ReactiveUI/CefSharp is initialized and it doesn't contain all the info/files it needs, it will not possible to add them later on runtime. But this is just guessing based on my experience.
I know it's an old thread, but just to save other developers time when facing this problem.
My solution was to add the following code in the entrypoint of the project that makes use of ReactiveUi and ReactiveUi.Wpf.
var reactiveUiWpfName = typeof(ReactiveUI.Wpf.Registrations).Assembly.FullName;
Assembly.Load(reactiveUiWpfName);
Of course, it was just required because I couldn't reference ReactiveUi or ReactiveUi.Wpf in my application startup project due to the project specifications, otherwise this error wouldn't appear anyway.
(Please, observe that, in your case you should use ReactiveUi.Winforms in the places I've used ReactiveUi.Wpf)

Why is my app error in design time, but not run time?

I am creating a WPF app using Ninject. I created my bindings in Application.xaml.vb and then store the kernal in Application.Current.Properties so I'll have those bindings to resolve as needed.
I am getting this error at design time: Cannot create an instance of "MainUserViewModel". If I remove the code out of my MainUserViewModel's constructor, I don't get the error.
Public Sub New()
'IoC is the kernel
Dim repository = IoC.Get(Of IUserRepository)()
_users = New ObservableCollection(Of User)(repository.GetAll())
End Sub
However, when I run the code, my the error goes away and my view populates just fine from _users. I would think if there's an error, the code wouldn't compile and (appear to) work.
I've removed every other piece of my VM and added them all back in. The error only appears when I use that line of code.
Can anyone explain why this is happening? I've checked the repository and all of the expected data is there.
EDIT
Is it possible that the error is occurring in the XAML due to the dependencies not being able to be resolved since it wasn't running? So, as far as it knows, that observable is never being initialized?
Probably the bindings for IoC are not initialized at design time and IoC.Get(Of IUserRepository)() is throwing NinjectActivationException thus the viewmodel cannot be created by designer.
I would move the init code from constructor e.g. to some lazy loaded property.
// sorry for C#
private ObservableCollection<User> _users;
public ObservableCollection<User>
{
get
{
if(_users == null){
repository = IoC.Get<IUserRepository>();
_users = new ObservableCollection<User>(repository.GetAll());
}
return _users;
}
}
However, it is not a good idea to wire up all your code with hard coded dependency on IOC container - it is commonly called "service locator anti-pattern". Give a try to approach in this link: Ninject constructor injection in WPF => there is also use of service locator, which will be responsible for creating your ViewModels, but the dependency on IoC is elegantly hidden in one place - composition root.

MVVM Light DispatcherHelper & Unit Testing w/MSTest

I considered putting this as an additional question in this thread:
Unit testing with MVVM Light & DispatcherHelper
but as I delved into it, I think it's a little different.
I have one project containing view models/views, a separate project containing models, and a third separate unit test project (MSTest) containing tests for the models. I'm using MVVM Light's DispatcherHelper to help me do work in a background task, but to get errors from that task back to the UI.
The view model/view project has the App.xaml.cs file, which contains the OnStartup event handler, in which I've put the DispatcherHelper.Initialize() call. One of the VMs invokes a call to one of the model's long-running methods on another thread using delegate.BeginInvoke():
FileProcessDelegate processor = GenerateAddressingFile;
processor.BeginInvoke(FileGenerationComplete, null);
In this long-running method, it calls a utility method to send errors back to the UI. These are in the form of mvvm-light messages that are sent using the DispatcherHelper.CheckBeginInvokeOnUI()
internal static void ReportErrorToUi(IFileError error)
{
DispatcherHelper.CheckBeginInvokeOnUI(
() => Messenger.Default.Send(new GenericMessage<IFileError>(error), MessageTokens.ReportFileError));
}
This all seems to work fine in the app.
In one of the unit test classes for the model, I attempt to check that the long-running method correctly sends messages when errors occur. In that test class, I have a call to DispatcherHelper.Initialize() in the MSTest class initialization method.
#region Additional test attributes
//
//You can use the following additional attributes as you write your tests:
//
//Use ClassInitialize to run code before running the first test in the class
[ClassInitialize]
public static void MyClassInitialize(TestContext testContext)
{
DispatcherHelper.Initialize();
}
The test registers for the message that should be sent, then calls the model's method directly, without invoking it on a separate thread. As I understand it, because the call to send the message is wrapped by DispatcherHelper.CheckBeginInvokeOnUI() in the model, it should do the right thing regardless of whether it is occurring on a background thread or on the UI thread.
In the first test of this class that checks the method, everything works fine. The test receives the error messages being sent back and passes with flying colors.
When subsequent tests in the same class run, the messages are never received by the test. When I've stepped through it on these subsequent tests, I notice that when I get to the DispatcherHelper.CheckBeginInvokeOnUI() method, the DispatcherHelper.UIDispatcher.Thread instance says it's dead.
Any thoughts?
I'm not sure if this solution is something that can be included in Mvvm Light, or if it violates some base premises of the framework, but here's what I did to get this to work:
I made a custom local version of the DispatcherHelper class that has one minor difference from what's in Mvvm Light, namely the logic in the Initialize() method:
public static void Initialize()
{
if (UIDispatcher != null &&
UIDispatcher.Thread.IsAlive)
{
return;
}
UIDispatcher = Dispatcher.CurrentDispatcher;
}
Here, the change is the addition of:
&& UIDispatcher.Thread.IsAlive
to the condition. This allows me to call DispatcherHelper.Initialize() in the MyTestInitialize() method of my unit test class. That way, if the previous thread that the UIDispatcher was associated with has died, the DispatcherHelper gets the new Dispatcher/Thread that this current test method will be run on.
Apparently, in the VSTS Testing framework, there is not a "central" thread that acts like the UI thread in an application. The MyClassInitialize() method was executed on one thread, and then the tests were run on a completely different thread. The Mvvm Light definition wouldn't let me hook the DispatcherHelper up to the new test thread by calling DispatcherHelper.Initialize() again because the UIDispatcher wasn't null, it's thread was just dead.
Testing async code is tricky. Typically, if it works once, and then not anymore, it sounds like you are not synchronizing your threads correctly. In your test method (main thread), are you waiting for the background thread to execute?
Jonas Follesoe wrote a few good articles about async unit testing in Silverlight, for example http://jonas.follesoe.no/2007/09/18/unit-testing-event-based-asynchronous-code/. This is typically how I unit test my async code too.
Hope this helps,
Laurent

Why does my Moq IEventAggregator verification fail?

I use Composite WPF(Prism) and I am trying to unit test that my Controller does in fact subscribe to a Composite Event.
My subscription code looks as follows...
//Init Events.
this.eventAggregator.GetEvent<PlantTreeNodeSelectedEvent>().Subscribe(
ShowNodeDetails, ThreadOption.UIThread);
My unit testing code looks as follows (I use Moq as my Mocking Framework and Unity as my DI Framework)...
Mock<PlantTreeNodeSelectedEvent> eventBeingListenedTo = new Mock<PlantTreeNodeSelectedEvent>();
eventAggregatorMock.Setup(e => e.GetEvent<PlantTreeNodeSelectedEvent>()).Returns(eventBeingListenedTo.Object);
//Initialize the controller to be tested.
IPlantTreeController controllerToTest = container.Resolve<IPlantTreeController>();
//Verify.
eventBeingListenedTo.Verify(
e => e.Subscribe(It.IsAny<Action<string>>(), ThreadOption.UIThread));
This subscribe method IS being called (I've verified by running with the debugger), but the Verify always fails with "Invocation was not performed on the mock: e => e.Subscribe..."
Any idea what I am doing wrong?
In your code, it seems like the eventAggregatorMock instance is never used. I would guess that you need to register it with the container so that it is being used by controllerToTest.
You seem to be testing too much in your unit test. You shouldn't need a container, you should just create your controller providing mock dependencies, because you should only test 1 thing in a unit test (you don't need to test that the DI framework works, as it usually does ;-)). It will also ensure that you provide the correct mocks, now it is not clear from your code as Mark Seemann has pointed out in his answer.
You may try to setup a method call under question in the beginning. Sometimes it seems to help moq to verify the class appropriately. In this case you may also want to setup your mock behavior to be Strict in the constructor, so that you will get the test failed for other, unexpected calls to your mock.
eventBeingListenedTo.Setup(e => e.Subscribe(It.IsAny<Action<string>>(), ThreadOption.UIThread));
use a mocking aggregator like this (for Rhino.Mocks)
http://adammills.wordpress.com/2010/12/13/auto-mocking-eventaggregator/
If you use ThreadOption.UIThread, it calls Dispatcher.Invoke which won't work without a Message Loop; which isn't normally running in unit tests.

Resources