How do I code a Xamarin.Forms view using pure TDD?

System.AshSystem.Ash AUMember ✭✭
edited April 2018 in Xamarin.Forms

I'm doing pure TDD and I want to test a view in isolation (check properties on it, make sure they are set correctly, etc).

For e.g, I might want to do something like (using BDD terminology):
Given a WelcomePage
Then it must have a label with text "Welcome!"

Or:
Given a WelcomePage
Then it must have a button with text "Start"
And this button's command must be StartCommand

Because it's pure TDD, there's not a single line of application code that I can write without a test to drive it.

So, my questions are:
1) Is it possible to unit test a view? It seems as if it's just not possible with XF due to its tight coupling with implementation (InitializeComponent(), Xamarin.Forms.Init(), all of which are expected to be called before properties on a view can be accessed).
2) If the answer to (1) is yes, how would I do it? Remember that while I can mock dependencies, I can't partially mock a class, as that defeats the purpose of unit testing.
3) If the answer to (1) is no, then is an integration test the only option to drive application code?

Tagged:

Answers

  • AlexPAlexP Member ✭✭

    I think that you are taking TDD too far.
    TDD is meant to check the logical part of your code, not the view part.

    Lets take as an example the default calculator app on different Windows itterations:

    The app looks changed through the different windows versions,
    But, although it looks changed, the logic behind stayed the same. (The implementation might change, but not the outcome).
    Therefor, you should be able to run the same tests as before, and pass them. Because regardless of the looks of the app and regardless of the implementation, 2 + 2 will still be 4 and 5 x 6 will still be 30.

    However, if you would create tests for the View part, you would have to rewrite a lot of the UI tests after each new version, which defies the purpose of unit testing completely.

  • System.AshSystem.Ash AUMember ✭✭
    edited April 2018
    Sorry, completely diagree. You either do pure TDD as it’s intended or you don’t. There’s no in-between here.

    If you write application code, you bloody well have a good reason for it since you’re spending the time (and getting paid for it). It’s not just ‘tests’, it’s the specs of how the program should work, which doesn’t just include functionality but also appearance. It’s a stakeholder requirement.

    With MVVM, there are 3 parts, so basiclly according to the way I see you writing applications, only about two-thirds of your application has actually been asked for.

    If my view changes from purple to yellow, I absolutely want my test (which expected purple) to fail. Else it’s my head at the next stakeholder presentation, because some idiot thought it’d be okay to make a colour change and this code made its way to prod because some idiot before him was under the impression that it was sufficient just to test the Model and ViewModel parts of an MVVM app.
  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    That level of strictness is going to frustrate you. Very few patterns get adhered to that stringently including MVVM.

    I loves my MVVM and tell people all the time here that they need to be using it instead of the junk they are doing. But truthfully most apps have some 'give and take. So often there is some whacky business need that causes even the best architects to have to deviate. We might call it "an MVVM hybrid" so it sounds good to make us feel better about deviating, but in the end it just means that no single pattern can work for every situation on every app for every need imaginable and so we need to be flexible enough to accept that.

    Then it must have a button with text "Start"

    So this is an English-only app? Half my UI doesn' even have text in it because the text comes from resources and changes depending on the language.

    That's part of the point @AlexP is trying to make: That UI can shift a lot. It suddenly becomes different for all sorts of wants and wishes from art director... but the rules of operation stay the same. Besides... UI is build and check and move on. If you spend 100 hours making some automated checker for something your boss can look at, and QA an confirm in 1 hour what's the point? Or just having a button that says "Start" is meaningless if it is off-screen when in Portrait orientation. A code test would pass it because it exists: Where a human would fail it because its off screen.

    There are just some things you have to let a person do - UI is one of them.

  • System.AshSystem.Ash AUMember ✭✭
    edited April 2018
    Um, it wouldn’t just be a test for the existence of a “Start” button, there would be a test for every single property, including layout. Existence was just one of ‘every’ possible piece of code that would be driven by a test. I think this is where people are missing the point of TDD when they start asking questions like “but what if it’s off-screen when in portrait mode?”. Well did the business requirement specify a portrait mode? (Add a test for it and then code it). Where did the requirement specify it should be when in portrait mode? (Add a test for it and then code). Hopefully I’m making myself clear here...

    Does it seem like a lot of work? Oh absolutely, but if you want to be lazy then perhaps TDD isn’t for you. You may revert to coding the old fashioned way where you write code that you have no proof of being correct, because the proof is the test which you don’t have.

    I don’t know how many stakeholder presentations you’ve given but every single one I’ve done working across multiple companies follows this same pattern: you spend weeks perfecting functionality (cause really as a programmer that’s the most fun part), and then the stakeholders sit there going “hmm, why is that button the wrong green?”.

    Furthermore, given that you’ve used the term ‘automated checker’, it sounds like another point you’ve missed is that of a ‘unit’ test. What you are talking is automated UAT. Not what I asked for if you read my question.

    A view is just a class, just like what a Model is and just like what a VM is, and as such should be unit tested the same way. Any exceptions to the rule here are certainly not defined by TDD. They may exist purely due to the tight coupling to implementation XF has, which is precisely what my question (1) was asking. Resource files are another framework related blocker that gets in the way of TDD, further justifying a “no” for question (1). People’s hesitation to unit testing views in .Net MVVM...yet another justification.

    The only way I can accept not driving view code with unit tests is if it’s a “no” to question (1).
  • JohnHardmanJohnHardman GBUniversity mod
    edited April 2018

    @System.Ash said:
    So, my questions are:
    1) Is it possible to unit test a view? It seems as if it's just not possible with XF due to its tight coupling with implementation (InitializeComponent(), Xamarin.Forms.Init(), all of which are expected to be called before properties on a view can be accessed).
    2) If the answer to (1) is yes, how would I do it? Remember that while I can mock dependencies, I can't partially mock a class, as that defeats the purpose of unit testing.
    3) If the answer to (1) is no, then is an integration test the only option to drive application code?

    Re. (1) - The definition of "unit" in unit testing has changed over time. Taking the older definition (which is the one I prefer), the answer would be - only by putting your own interface-based framework between Xamarin.Forms and your application code, so that you can mock out everything but the code under test, and effectively duplicate some areas of XF if using XAML. So, realistically no, unless you are willing to add that framework. The newer definition of "unit" (which I accept but don't really like) would test a unit of functionality, including use of Xamarin.Forms code and any third-party code required to implement that unit. If you use that definition, the answer is yes.

    From your comments, I assume you go with the older definition of "unit" (as I prefer). On that basis, the answer to (1) is realistically no (unless you want to build the framework mentioned). So, moving on to (3), the answer is yes.

    As for the other discussion in previous posts, there is a balance to be had. Where that balance is set depends on many things, including budget, time pressures, past experience etc. The primary question has to be, does the application implement the functional requirements such that the users can get done whatever they expect the app to do (this is what @ClintStLaurent is correctly prioritising). Now, to get done whatever is expected, requires interacting with controls/views on the page. If they are outside the viewable area, and cannot be scrolled to, and the page content cannot be made smaller to bring them into view, and changing orientation won't bring them into view, then the user cannot interact with the controls and so cannot get done what is required. It's a simple wrapper over a UI automation tool (e.g. Ranorex, Xamarin.UITest etc) to check that any UI interaction can be done because the control/view is already visible or can be made visible by one of those mechanisms. That's a sensible thing to include even in the primary test automation. Whether you want to check minimum sizes of controls/views, whether text has been clipped, or whether fonts have been reduced to such a small size to make them unreadable, is an extension of that wrapper. If I were building that wrapper, I'd probably include the option to check those things.

    Where things get fuzzier is/are non-functional requirements such as supporting accessibility. Assuming the application explicitly implements support for accessibility, it's reasonable to automate testing of that. If a page normally uses green on red (yes, I've encountered an analyst who specified that in the requirements many years ago), then you'd want to be able to switch to a different theme if you cannot distinguish green from red. It's not unreasonable in that case to test that those changes are made when so configured. How you prioritise implementing those tests is the open question.

    Automating testing of localisation is sensible. Whether the tests and the code under test share the same resources is the next question. If I'm operating as a one-person outfit, then I'd share the resources. If I were working in a dedicated test automation team, not as part of the app development team, then I'd lean towards building my own resources based on the requirements.

    Back to colors - Where not considering NFRs such as accessibility, would I automate testing that something is always yellow and that something else is always blue. Nope, not if those colored objects will never change color and the colors used don't have any particular significance. That's a one-time check done by eye. If post-release, changes can only be made to code as a result of a documented work item, and any commits to the code being built are linked to uniquely numbered work items, then there should complete traceability from requirement to code change (ok, there might be more layers, but you get the idea). That means that if anybody changes a color, without the work item having specified the change, the developer is operating off-piste and needs to be brought back on track. Yes, that requires somebody to notice, but in the scenario described it's such a low priority that I cannot think of a project manager or product owner who would pay for the development time to implement test automation of that outside of safety critical work. Even then, it may not happen.

  • System.AshSystem.Ash AUMember ✭✭
    edited April 2018

    Thanks @JohnHardman.

    Okay, now we are finally getting somewhere with this discussion, since my exact questions are being addressed. Appreciate the philosophical input from everyone else, but really, any issues with budget/time and such are really not the point here. In fact, it's costing me those things just debating those things...

    John, could you elaborate more on

    The newer definition of "unit" (which I accept but don't really like) would test a unit of functionality, including use of Xamarin.Forms code and any third-party code required to implement that unit.

    I'm not sure I'm interpreting it the right way.

    Regarding,

    only by putting your own interface-based framework between Xamarin.Forms and your application code, so that you can mock out everything but the code under test, and effectively duplicate some areas of XF if using XAML

    Yea, I suspected as much.

    Back to colours - Given the locale is Australia Then colors should be colours (hm, seems as if the spell checker got that wrong since I'm seeing red squiggles, probably because there was no unit test on this view ;) ) ...
    John, I get what you're saying and I've worked in environments where that is the case, where a colour change (for e.g) will only be done if a work item requires it. But there have been countless times where this is broken. Not intentionally, but accidentally. And typically it's not a simple string change in one place which is easy to pick up. No, typically what causes headache tends to be more general changes somewhere (in some class...) that then goes on to affect a whole bunch of other view components. Now the reviewer will look at this change from the point of view of what the work item requires and approve it as long as it achieves that. Next thing you know, you have faulty code in prod.
    Fundamentally, there are several things wrong with this method, including:
    a) You are going backwards in methodology. Proper use of TDD doesn't just mean you have proof of correctness, but also a test suite to automatically test for such regressions. So budget wise, this is actually beneficial. Potentially spending hours of work trying to figure out why things suddenly aren't the right shade of green, having to troubleshoot with stakeholders, etc, and re-deploying (hoping of course that nothing else is broken). All of which could've been avoided pretty easily (and automatically) by your unit tests.
    b) You are getting a human (reviewer) to do what should be the machine's job. Hey, isn't this part of the reason why we have computers? To take away monotonous and error-prone tasks away from people? Is a reviewer going to check every single existing UI requirement (not just colours) is satisfied looking at a code change? Is that even their job as a reviewer?

  • JohnHardmanJohnHardman GBUniversity mod

    @System.Ash said:
    John, could you elaborate more on

    The newer definition of "unit" (which I accept but don't really like) would test a unit of functionality, including use of Xamarin.Forms code and any third-party code required to implement that unit.

    The newer definition of "unit" relates to a "unit of work". So, if the unit of work is to implement a dashboard that summarises data from a database, the unit in MVVM-speak contains the View, ViewModel and Model relating to that piece of work, together with the database tables that are involved, the data access layer, any style setters etc. The unit would not involve the CRUD functionality used for adding, viewing, updating or deleting database content, as those would be different units of work.

    From that type of definition, you can see why old-school types like me think of that as an integration test rather than a unit test. For the old-school definition, we'd be mocking out everything but a single class under test.

    Because the definition of a "unit" test has changed over time, I adapt terminology depending on who I am speaking with.

    I adapt color vs. colour in the same way. In the forums and in my code I use American English rather than British English, with a few exceptions - I throw in "whilst" in the forums to confuse people :-)

  • System.AshSystem.Ash AUMember ✭✭
    > The newer definition of "unit" (which I accept but don't really like) would test a unit of functionality, including use of Xamarin
    > I adapt color vs. colour in the same way. In the forums and in my code I use American English rather than British English, with a few exceptions - I throw in "whilst" in the forums to confuse people :-)

    Lol, fair enough : - )

    Ah, I see now what you mean by the newer definition of “unit”, and yes, given that definition, I would be old-school.

    Also, yes, I too see it as an integration test. BDD specs tend to have the same form, where a spec will describe (for e.g) everything from what happens when a user clicks a button to what data should be added to a DB. Personally when I see such specs, my mind goes “mock mock mock” and I proceed with breaking the elephant down into the smallest pieces of work possible. At the same time, I respect the original spec by still maintaining it as an integration test (for later).

    Coming back to unit testing views, I had a ponder about what you suggested regarding an intermediate framework which mocks XF. While do-able, it’s certainly a fair bit of work, especially since XF views don’t even use Dependency Injection (unless I’m mistaken?).
  • JohnHardmanJohnHardman GBUniversity mod

    @System.Ash said:
    Coming back to unit testing views, I had a ponder about what you suggested regarding an intermediate framework which mocks XF. While do-able, it’s certainly a fair bit of work, especially since XF views don’t even use Dependency Injection (unless I’m mistaken?).

    Yes, it would be a serious amount of work. I wouldn't do it. It would be good that if with a major version number change coming (i.e. XF 3.0) some aspects of XF could be changed, including testability (more interfaces, less concrete classes) and asynchronous support (virtual methods in the XF API returning Task instead of void). I haven't looked at the pre-release 3.0, but I am assuming that neither will be done.

  • shamseenshamseen Member ✭✭

    hi, sorry to interject

    what frameworks are you using for dependency injection, mocking, and testing?

  • System.AshSystem.Ash AUMember ✭✭
    > @shamseen said:
    > hi, sorry to interject
    >
    > what frameworks are you using for dependency injection, mocking, and testing?

    DI: Autofac
    Mocking: Moq
    Testing: xUnit
Sign In or Register to comment.