views:

50

answers:

4

I've got a C# form, with various controls on it. The form controls an ongoing process, and there are many, many aspects that need to be right for the program to run correctly.

Each part can be unit tested (for instance, loading some coefficients, drawing some diagnostics) but I often run into problems that are best described with an example:

"If I click here, then here, then change this, then re-open the form, then click here, it crashes or produces an error"

I've tried my best to use common code organisational ideas (inheritance, DRY, separation of concerns) but there never seems to be a way to test every single path, and inevitably, a form with several controls will have a huge number of ways to execute.

What can I read (preferably online) that addresses this kind of issue, and is there a (non-generic) term for it. This isn't a specific problem I'm having, but one that creeps up on me, especially with WinForms.

A: 

I'd try and map common user UI workflows (with slight deviations, perhaps using "foreach" on control lists to pretend you're a user spamming everywhere and changing stuff) as unit tests.

I wouldn't go as far as (click at (x,y)) but more firing events like "txtUsername_Focused", "txtUsername_TextChanged", "btnBack_Click", "btnForward_Click", "btnSave_Submit", of course with some handling if you get different forms showing as a result.

Graphain
Interesting idea, but this gets complicated if each fired event takes a long time, with various states during execution. Is there an easy way to create a testing scheme like you're describing, or am I going to have to create custom methods to call all the buttons with timers in between?
Carlos
Not that I'm aware of (which doesn't mean there isn't). I'd have to assume there's a framework somewhere that tries to break your UI for you. I guess the issue is you want to break it in particular ways and not others and you also want to test for "expected errors" and "expected results". I don't think you want timers - you could probably get the call to block as the event processes (i.e. rather than raising the event, call the event delegates yourself).
Graphain
+1  A: 

Each of those principles (inheritance, DRY, separation of concerns) is not a guaranteed recipe for high quality code, of course. If you use your inherited and DRY method to divide by zero, then you're still doing something wrong.

My advice for hard-to-trace errors: logging! Log the internal state of variables at key points in the user's steps to reproduce the problem.

ErikHeemskerk
Logging is essential.
Graphain
Constant logging is my current way to solve the issue, actually. But of course that means waiting for an error to happen and trying to fix it, rather than having a method that reduces such errors.
Carlos
+1  A: 

You're trying to do acceptance testing, not unit. It is useful to test whether all bricks of your system are properly connected together. But bricks themselves should be tested with unit-tests.

So if you have a functionality that takes some coefficients and makes diagram, test it separately from GUI from all sides. Give it all possible edge-cases of coefficients and test point coordinates it returns. It was just one unit, there will be dozens, hundreds or thousands.

After you're sure in your units, do few functional/integrational/acceptance tests to make sure your units play well together.

For unit testing you can use NUnit or built-in test system. For acceptance testing look at FITnesse or search for commercial products.

And to get an idea of how to divide the application in units, read about MVC and similar architecture solutions.

nailxx
A: 

I have faced this problem (test every single path) with Windows Forms. But after moving to WPF,Silverlight the problem is solved. Use MVVM along with command pattern (basically ensuring that you have no code in code-behind files). Ensure that you have single responsibility classes. In your unit tests, you can simulate every scenario including button clicks and you will be able to test all possible paths. I am not sure how you will be able to use MVVM and command pattern in WinForms. But, I am google will definitely provide you with some answers.

P.K