Unit testing ViewModels

noutramnoutram Member ✭✭
edited July 26 in Xamarin.Forms

I appreciate this is a bit of a general question, but I thought someone might be able to direct me.

I've written a sample application to show my students an example of MVVM . One of the motivations for MVVM is loose coupling and hence easier unit testing.

For my ViewModel, everything is working out so far except for one small detail - Commands.

In my view model constructor, I have the following lines which are dependent on Xamarin.Forms:

  1. In the constructor of the ViewModel

    FetchNextSayingCommand = new Command(execute: async () => await DoFetchNextMessageCommand(), canExecute: () => NetworkIsIdle);

where DoFetchNextMessageCommand() simply asks the model to fetch a new string from Azure and NetworkIsIdle is a bool property that is false during a network transaction.

  1. When listening for an event from the Model to say the network status has changed:

    (Command)FetchNextSayingCommand).ChangeCanExecute()

I think my question reduces down to two fundamentals:

  1. Should a ViewModel aspire to have no (tightly coupled) dependencies on Forms or am I simply being too purist?
    (i.e. such that I can comment out using Xamarin.Forms; from the head of my source)
  2. Is there a considered best-practice for unit testing Commands?
    (I have my own ideas, but I suspect someone else has already solved this)

Many thanks,

Nick

Best Answer

Answers

  • NMackayNMackay GBInsider, University mod

    We test all

    • ViewModels
    • Services
    • Repository layers
    • Helpers
    • Even Converters sometimes

    This library is really useful if you have ViewModel code that needs Xamarin Forms initialised if you have Device.BeginInvokeOnUIThread etc (Although you could abstract that away to an interface like Prism and Xamarin Essentials Interfaces does

    https://www.nuget.org/packages/Xamarin.Forms.Mocks/

    Our project requires 90% test coverage which is pretty high! :o

  • NMackayNMackay GBInsider, University mod

    As your commands, you shouldn't test the command as it's unreliable especially in automated branch testing,

    If you write your commands like this it is testable

    CancelCommand = new DelegateCommand(async () => await Cancel()).ObservesCanExecute(() => CanExecute);
    
     internal async Task Cancel()
     {
         DoSomething async that can be tested
    
  • noutramnoutram Member ✭✭

    @NMackay said:
    As your commands, you shouldn't test the command as it's unreliable especially in automated branch testing,

    If you write your commands like this it is testable

    CancelCommand = new DelegateCommand(async () => await Cancel()).ObservesCanExecute(() => CanExecute);
    
     internal async Task Cancel()
     {
         DoSomething async that can be tested
    

    I think you lost me there :blush:

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    I actually shy away from using Commands because they make testing difficult. I would much rather have a public async method that I can call and await from either a click event handler or a test. But for when I do use commands, I defined an AsyncCommand class that adds an ExecuteAsync method.

  • noutramnoutram Member ✭✭

    @JohnHardman said:

    @noutram said:
    1. Should a ViewModel aspire to have no (tightly coupled) dependencies on Forms

    Aspire to, yes.

    @noutram said:
    or am I simply being too purist?

    It depends on how likely you think it is that you would ever change the View layer to use something other than Xamarin.Forms. If you think that could happen, then you are not being too purist. If you think it will never happen, then it's a question of pragmatism vs. purism (is that a word?) - one for your own conscience.

    Having used Xamarin.Forms since version 1.x, I've had to workaround so many defects in so many parts of Xamarin.Forms, that I have ended up abstracting many parts of Xamarin.Forms. That has allowed me to continue working when something in Xamarin.Forms is broken, and allows me to do things such as use different versions of Xamarin.Auth when targeting different platforms (when particular versions of Auth worked on one platform but not another). A consequence of this is that I have a much reduced amount of tight coupling on Forms and other Xamarin NuGets. It's more work, but it has benefits.

    @noutram said:
    1. Is there a considered best-practice for unit testing Commands?

    I'm not aware of a documented best practice. As you are asking this question, I suspect you've probably already read "The Art of Unit Testing: with Examples in .NET" by Roy Osherove. If not, then you might find it interesting. As for referencing Command, I would advise wherever possible to avoid passing around a concrete class and instead use the ICommand interface. When instantiating a Command (or something derived from ICommand), I would advise having a constructor to which you pass anything required in the form of interfaces rather than passing in concrete classes. Again, there is a balance to be found - the return on investment of the extra work required to make Commands and View Models unit-testable will be worth it on some projects, but not on others - it depends on who will be using the app, for how long, what the financial or reputational damage would be if it goes wrong etc.

    Very helpful. Many thanks. I'm only writing a simple example for my students, and I'm making great efforts to keep the code clear and simple for that reason - as it's so simple, elaborate steps to make it fully testable are probably not justified except maybe in an educational sense. I went down the route of of mocking the Command object, only it created so much code and complexity, I (once again) lost the simplicity and clarity that is so important. Instead, I've decided to be less purist, but still create a single interface IMainViewHelper (passed into the constructor of the VM), which makes reference back to view-specific methods in the View object (such as instantiation of Command, alerts and some simple navigation). I should probably use multiple interfaces and stick to design patterns, but as I'm no design pattern / OO guru, again, pragmatism wins (and it's so much easier to admit I'm not a OO expert than try and be perfect ;)

  • NMackayNMackay GBInsider, University mod

    " which makes reference back to view-specific methods in the View object (such as instantiation of Command, alerts and some simple navigation)"

    TBH, all that should be in the viewmodel.

  • JohnHardmanJohnHardman GBUniversity mod

    @noutram said:
    create a single interface IMainViewHelper (passed into the constructor of the VM), which makes reference back to view-specific methods in the View object (such as instantiation of Command, alerts and some simple navigation)

    I'm slightly concerned by that sentence. What you might want to do, is once you have some example code, post it here to get input on how it could be improved. Navigation is the one where people are most likely to disagree - some people do it in the View Model, some do it in the View. There are various ways of having an app navigate from one page to another - doing it based on state transition rather than having lots of hard-coded Navigation.PushAsync calls scattered throughout the code is nicer IMHO, but even doing it based on state transition there are many possible implementation methods.

    In a teaching environment, you can of course present the standard Xamarin.Forms bits - Commands, MessagingCenter, Navigation, etc., and separately explain about mocks/fakes/stubs and their use with interfaces in unit testing, then leave putting it all together to the students. That might sound harsh on the students, but even introducing mocks/fakes/stubs goes beyond the BSc that I completed in my spare time a couple of years ago.

  • noutramnoutram Member ✭✭

    @NMackay said:
    " which makes reference back to view-specific methods in the View object (such as instantiation of Command, alerts and some simple navigation)"

    TBH, all that should be in the viewmodel.

    Ok, that's interesting, as that is where I started from. I then went down the path of removing any reference to any concrete Forms classes in the VM (with unit testing in mind). My observation is that it all added complexity, and in an educational context, that rarely goes well. When I wrote MVC for iOS, all the UI-state logic went in the controller, including state machines, and the view was (ultimately) a NIB file.

    As someone new to Xamarin, it's surprisingly difficult to get a firm grasp on architecture and best practise. For a small project, I would not care, but when you're teaching (in 6 months time), it's my job to get it right. Any suggestions of good sites. / books that I could read ?

  • noutramnoutram Member ✭✭

    @JohnHardman said:

    @noutram said:
    create a single interface IMainViewHelper (passed into the constructor of the VM), which makes reference back to view-specific methods in the View object (such as instantiation of Command, alerts and some simple navigation)

    I'm slightly concerned by that sentence. What you might want to do, is once you have some example code, post it here to get input on how it could be improved. Navigation is the one where people are most likely to disagree - some people do it in the View Model, some do it in the View. There are various ways of having an app navigate from one page to another - doing it based on state transition rather than having lots of hard-coded Navigation.PushAsync calls scattered throughout the code is nicer IMHO, but even doing it based on state transition there are many possible implementation methods.

    Thanks for the response. I too am concerned that I've still not got a clear picture of how Forms apps should be best architected. I have a draft blog where I can share some stuff (If you have the time to look at it). It's draft, so I was not ready to release it to the wider world - I could add you if you like.

    In a teaching environment, you can of course present the standard Xamarin.Forms bits - Commands, MessagingCenter, Navigation, etc., and separately explain about mocks/fakes/stubs and their use with interfaces in unit testing, then leave putting it all together to the students. That might sound harsh on the students, but even introducing mocks/fakes/stubs goes beyond the BSc that I completed in my spare time a couple of years ago.

    I agree. My gut feeling is that I need to distill this right down to a level where students can ingest without too much angst, cover a representative span of the API, and leave them to work out specific API details on a needs basis. However, a comparison of architectural choices is something they probably need direction on. However, that is 6 months away. In the interim, I'm trying to get a grip on how Forms apps should/can be architected. When I taught iOS, I already had a few apps under my belt and a fairly good grasp of how things (should) fit together. For Forms, it's been more challenging as there seems to be less consensus or clarity, unless I'm missing something.

  • noutramnoutram Member ✭✭

    @JoeManke said:
    I actually shy away from using Commands because they make testing difficult. I would much rather have a public async method that I can call and await from either a click event handler or a test. But for when I do use commands, I defined an AsyncCommand class that adds an ExecuteAsync method.

    I can understand that, and that is an option I can present to students of course. There is always the risk of being purest for the sake of it. Pragmatism often has a place :)

  • JohnHardmanJohnHardman GBUniversity mod
    edited August 12

    @noutram said:
    Thanks for the response. I too am concerned that I've still not got a clear picture of how Forms apps should be best architected. I have a draft blog where I can share some stuff (If you have the time to look at it). It's draft, so I was not ready to release it to the wider world - I could add you if you like.

    Happy to take a look, but cannot commit to how quickly that might happen.

    I'm trying to get a grip on how Forms apps should/can be architected.

    MVVM is the basic architecture to go with. Be pretty strict about sticking to it, otherwise different layers quickly get polluted.
    Of course, once building apps that include not just local data storage (typically Sqlite), but also having offline synch with a database in the cloud, data being shared (subject to authentication/authorisation) with other users, and push notifications to handle updates done by other users, the required architecture goes well beyond basic MVVM.

    I've read most existing, hardcopy books about Xamarin.Forms, and still haven't found one that I think really covers architecture well, not even just for basic MVVM, let alone all of the offline synch and data sharing stuff. If only I had the time... TBH, I had hoped that when Xamarin recruited Charles Petzold, that he might be tasked with doing that.

    BTW, if you haven't read it already, take a look at https://aka.ms/xamarinpatternsebook

    Xamarin/Microsoft's documentation at https://docs.microsoft.com/en-us/xamarin/ is also much better than it used to be.

    cc @DavidBritch

  • noutramnoutram Member ✭✭

    @JohnHardman said:

    @noutram said:
    Thanks for the response. I too am concerned that I've still not got a clear picture of how Forms apps should be best architected. I have a draft blog where I can share some stuff (If you have the time to look at it). It's draft, so I was not ready to release it to the wider world - I could add you if you like.

    Happy to take a look, but cannot commit to how quickly that might happen.

    I'm trying to get a grip on how Forms apps should/can be architected.

    MVVM is the basic architecture to go with. Be pretty strict about sticking to it, otherwise different layers quickly get polluted.
    Of course, once building apps that include not just local data storage (typically Sqlite), but also having offline synch with a database in the cloud, data being shared (subject to authentication/authorisation) with other users, and push notifications to handle updates done by other users, the required architecture goes well beyond basic MVVM.

    I've read most existing, hardcopy books about Xamarin.Forms, and still haven't found one that I think really covers architecture well, not even just for basic MVVM, let alone all of the offline synch and data sharing stuff. If only I had the time... TBH, I had hoped that when Xamarin recruited Charles Petzold, that he might be tasked with doing that.

    BTW, if you haven't read it already, take a look at https://aka.ms/xamarinpatternsebook

    Xamarin/Microsoft's documentation at https://docs.microsoft.com/en-us/xamarin/ is also much better than it used to be.

    cc @DavidBritch

    Hi John

    In case it's of any interest, here is my taught course so far (warts and all). This is pitched at stage-2 undergraduate students who have some experience of C# and Design Patterns (covered by others before me). The key thing is not to lose them, but give scope for the more ambitious to explore APIs and push beyond what I cover.

    https://github.com/UniversityOfPlymouthComputing/MobileDev-XamarinForms/tree/master/docs

    Sample code is here

    https://github.com/UniversityOfPlymouthComputing/MobileDev-XamarinForms/tree/master/code

    Right now, Chapter 2 is what I am tidying up. Thanks for the reference to the Enterprise Application Patterns book (by @DavidBritch). Although I'm on leave right now, that might be useful reading over the next couple weeks.

    Note the repo is currently public. The plan is to leave it as such (but not draw too much attention to it as it's all subject to change).

    Nick

  • JohnHardmanJohnHardman GBUniversity mod

    @noutram - Plymouth UK or Plymouth USA ?

  • noutramnoutram Member ✭✭
    > @JohnHardman said:
    > @noutram - Plymouth UK or Plymouth USA ?

    UK
  • JohnHardmanJohnHardman GBUniversity mod

    @noutram said:
    > @JohnHardman said:
    > @noutram - Plymouth UK or Plymouth USA ?

    UK

    Good to hear - sounds like Plymouth is way more up to date than the university I recently completed a BSc with in my spare time. Let's just say that other than one module that contained a tiny bit of JavaScript, the rest was either Visual Basic (I kid you not), or really basic Java. What you have been asking about here is significantly more real-world (I have 37 years development experience - the BSc was for other purposes).

  • noutramnoutram Member ✭✭
    > @JohnHardman said:
    > @noutram said:
    > > @JohnHardman said:
    > > @noutram - Plymouth UK or Plymouth USA ?
    >
    > UK
    >
    >
    >
    >
    >
    > Good to hear - sounds like Plymouth is way more up to date than the university I recently completed a BSc with in my spare time. Let's just say that other than one module that contained a tiny bit of JavaScript, the rest was either Visual Basic (I kid you not), or really basic Java. What you have been asking about here is significantly more real-world (I have 37 years development experience - the BSc was for other purposes).

    We are quite big on employability. Quite a few of us are hands-on nerds. My own background is mostly electronics - any CS knowledge I have is mostly self taught (since the age of 12). Naturally I don't get invited to many parties ;)
  • JohnHardmanJohnHardman GBUniversity mod

    @noutram said:
    In case it's of any interest, here is my taught course so far (warts and all). This is pitched at stage-2 undergraduate students who have some experience of C# and Design Patterns (covered by others before me). The key thing is not to lose them, but give scope for the more ambitious to explore APIs and push beyond what I cover.

    Would you like feedback on the materials? If so, how would you like to receive it?

  • noutramnoutram Member ✭✭
    > @JohnHardman said:
    > (Quote)
    > Would you like feedback on the materials? If so, how would you like to receive it?

    Hi

    They're very much in draft right now, but if you willing and have time, then I'd be grateful for any feedback whether general or detailed.

    As for the mechanism, whatever is most efficient for you - via github, email ([email protected]) or some other means.

    Many thanks

    Nick
Sign In or Register to comment.