[Beginner] Mocking UI controls?

96 views
Skip to first unread message

Urhgan

unread,
Oct 30, 2009, 9:56:26 PM10/30/09
to Moq Discussions
Hi,

I'm new to both unit testing and mocking, but I've read a lot the past
2 days about both. The problem I have is that almost every example has
a textbox with an interface that defines a get/set for it. The
controller then, through the interface, gets the text from the textbox
and does something and then could set the textbox if necessary.

That example is so straightforward and obvious.

When I'm trying to write unit tests for a small project of mine (for
testing mocking/unit tests), I immediately get stuck. Let me explain
one of the issues I have.

A function takes two arguments: ImageList and TreeView. The function
uses the images (icons) in the ImageList and applies them to the
different parts of the TreeView so that it has icons in it.

How would I test that?

I'm guessing I'm doing everything wrong since I can't find anything on
Google about mocking user interface controls. I'd be very glad if
someone could point me in the right direction.

Thanks.

Emanuele DelBono

unread,
Oct 31, 2009, 4:00:02 AM10/31/09
to moq...@googlegroups.com
Usually you don't test user interface "logic", but you try to move out
the logic in the controllers, that's why in the examples you saw the
interfaces.

Consider that of an object you can mock only the virtual and the
abstract methods and if you take a treeview or an ImageList I don't
know what you can mock...I think most of their members are not
virtual.

So, try to extract an interface from you UserControl that has very
simple methods like (AddImage, GetNode, etc...) and no logic (you
don't have if, for, foreach in the UI).
Now you can mock the interface and test the controller logic.

HTH...anyway if you want you can post some code to get a real example.

ema
http://blog.codiceplastico.com

Urhgan

unread,
Oct 31, 2009, 3:16:52 PM10/31/09
to Moq Discussions
Developing in this way is kind of strange for me (since it's new). I
can see the good things it yields by moving the logic to the
controller, and using interfaces for communicating. It makes it easier
to test, and also easier to switch the technology for the View (right
now WinForms, could easily change to ASP.NET, since the communication
is through the interfaces).

I wasn't sure what exactly you could mock, but I think I was heading
in your direction (virtual/abstract only).

I'll post my OnStartup code. Note that I kind of abandoned this whole
concept since I couldn't get it working, and I now want to rewrite
this piece to make it mockable. Right now it's a mix of... I don't
know what!

(MainForm implements the IMainView interface)
private void MainForm_Load(object sender, EventArgs e)
{
Controller.OnStartup();
}

(OnStartup exists in the MainViewController, that takes one parameter
of type IMainView in the constructor)
(Through the View, the UI can be accessed)
public void OnStartup()
{
VistaAPI.TreeViewStyles styles = VistaAPI.TreeViewStyles.None;

styles = VistaAPI.TreeViewStyles.AutoScroll;
styles |= VistaAPI.TreeViewStyles.FadeInOut;
styles |= VistaAPI.TreeViewStyles.SemiTransparentSelection;

//I should probably add IntPtr GetLibraryHandle to the interface
and get { return Library.Handle; } in the UI?
//By the way, "Library" is the TreeView
VistaAPI.TreeView.SetStyle(View.Library.Handle, styles);

ImageList icons = TreeViewIconSet.GetIcons(View.IconPath);
//Here I am lost...
TreeViewIconSet.ApplyIcons(icons, View.Library);
}

public static ImageList GetIcons(string iconPath)
{
ImageList icons = new ImageList();

icons.Images.Add("Administration", Image.FromFile(iconPath +
@"\Administration.ico"));
icons.Images.Add("Add", Image.FromFile(iconPath + @"\Add.ico"));
icons.Images.Add("Delete", Image.FromFile(iconPath +
@"\Delete.ico"));
icons.Images.Add("Search", Image.FromFile(iconPath +
@"\Search.ico"));
icons.Images.Add("Library", Image.FromFile(iconPath +
@"\Library.ico"));
icons.Images.Add("Folder", Image.FromFile(iconPath +
@"\Folder.ico"));
icons.Images.Add("Document", Image.FromFile(iconPath +
@"\Document.ico"));
icons.Images.Add("Unlock", Image.FromFile(iconPath +
@"\Unlock.ico"));

icons.ColorDepth = ColorDepth.Depth32Bit;

return icons;
}

public static void ApplyIcons(ImageList iconList, TreeView treeView)
{
if (iconList == null || treeView == null)
throw new ArgumentNullException((iconList == null ?
"iconList" : "treeView"));

if (iconList.Images.Count != 8)
throw new ArgumentException("All icons not found in the
ImageList (should be 8)", "iconList.Images.Count");

if (treeView.Nodes.Count != 2)
throw new ArgumentException("Not enough root nodes in the
TreeView", "treeView.Nodes.Count");
else
{
if (treeView.Nodes[0].Nodes.Count != 3)
throw new ArgumentException("Not enough sub nodes in the
first node of the TreeView", "treeView.Nodes[0].Nodes.Count");
}

treeView.ImageList = iconList;

TreeNode adminRoot = treeView.Nodes[0];
adminRoot.ImageIndex = treeView.ImageList.Images.IndexOfKey
("Administration");
adminRoot.SelectedImageIndex = adminRoot.ImageIndex;

TreeNode[] adminChildren = { adminRoot.Nodes[0], adminRoot.Nodes
[1], adminRoot.Nodes[2] };
adminChildren[0].ImageIndex = treeView.ImageList.Images.IndexOfKey
("Add");
adminChildren[0].SelectedImageIndex = adminChildren[0].ImageIndex;
adminChildren[1].ImageIndex = treeView.ImageList.Images.IndexOfKey
("Delete");
adminChildren[1].SelectedImageIndex = adminChildren[1].ImageIndex;
adminChildren[2].ImageIndex = treeView.ImageList.Images.IndexOfKey
("Search");
adminChildren[2].SelectedImageIndex = adminChildren[2].ImageIndex;

TreeNode libraryRoot = treeView.Nodes[1];
libraryRoot.ImageIndex = treeView.ImageList.Images.IndexOfKey
("Library");
libraryRoot.SelectedImageIndex = libraryRoot.ImageIndex;
}

I hope I didn't scare you away with the code, haha.

Thanks for your help so far.

On Oct 31, 9:00 am, Emanuele DelBono <codiceplast...@gmail.com> wrote:
> Usually you don't test user interface "logic", but you try to move out
> the logic in the controllers, that's why in the examples you saw the
> interfaces.
>
> Consider that of an object you can mock only the virtual and the
> abstract methods and if you take a treeview or an ImageList I don't
> know what you can mock...I think most of their members are not
> virtual.
>
> So, try to extract an interface from you UserControl that has very
> simple methods like (AddImage, GetNode, etc...) and no logic (you
> don't have if, for, foreach in the UI).
> Now you can mock the interface and test the controller logic.
>
> HTH...anyway if you want you can post some code to get a real example.
>
> emahttp://blog.codiceplastico.com

Emanuele DelBono

unread,
Nov 1, 2009, 1:58:51 AM11/1/09
to moq...@googlegroups.com
Hi!
Your code didn't scare me ;-)

The direction is right but not yet completed.
The methods in the controller should not work with concrete object of
the UI (TreeView, ImageList, ...) because doing this way you can't
easily test your controller and you are tied to the Winform
implementation.

Your controller get the IMainView interface in the constructor, and
that right. Then it should use that interface to communicate with your
view. For example to add the icons to the list:

public void GetIcons(String iconPath)
{
_mainView.SetImage(iconPath + @"\Administration.ico");
// ... others icons
}


In this way you can mock your view and verify that the SetImage method
is called the right number of time with right values.

[Test]
public void SetIconTest()
{
Mock<IMainView> view = new Mock<IMainView>();
Controller c = new Controller(view.Object);

c.GetIcons("iconPath");

view.Verify(v => v.SetImage(iconPath + @"\Administration.ico");

}

HTH, I know it's not easy, I had the same doubts when I started wiht TDD.
A final note: don't use static methods, they give you problems in the tests.


ema
http://blog.codiceplastico.com

Urhgan

unread,
Nov 1, 2009, 9:30:55 PM11/1/09
to Moq Discussions
Hello again,

I've rewritten this post multiple times now since I got stuck and was
going to ask for help, and while writing here, I came up with an idea
that worked!

Mocking loading/applying icons now works. I don't know if my solution
is acceptable though. I used your example as a template and changed a
few things:

The interface (IMainView):
void SetIcon(string iconPath, string iconName);

The controller:
private void ApplyIcons(string path)
{
View.SetIcon(path + @"\Administration.ico", "Administration");
View.SetIcon(path + @"\Add.ico", "Add");
View.SetIcon(path + @"\Delete.ico", "Delete");
View.SetIcon(path + @"\Search.ico", "Search");
View.SetIcon(path + @"\Library.ico", "Library");
View.SetIcon(path + @"\Folder.ico", "Folder");
View.SetIcon(path + @"\Document.ico", "Document");
View.SetIcon(path + @"\Unlock.ico", "Unlock");
}

The View aka GUI:
public void SetIcon(string iconPath, string iconName)
{
if (LibraryTreeView.ImageList == null)
LibraryTreeView.ImageList = new ImageList() { ColorDepth =
ColorDepth.Depth32Bit };

LibraryTreeView.ImageList.Images.Add(iconName, Image.FromFile
(iconPath));
int iconIndex = LibraryTreeView.ImageList.Images.IndexOfKey
(iconName);
TreeNode treeNode = null;

switch (iconName)
{
case "Administration":
treeNode = LibraryTreeView.Nodes["Administration"];
break;

case "Add":
treeNode = LibraryTreeView.Nodes["Administration"].Nodes
["Add"];
break;

case "Delete":
treeNode = LibraryTreeView.Nodes["Administration"].Nodes
["Delete"];
break;

case "Search":
treeNode = LibraryTreeView.Nodes["Administration"].Nodes
["Search"];
break;

case "Library":
treeNode = LibraryTreeView.Nodes["Library"];
break;

default:
break;
}

if (treeNode != null)
{
treeNode.ImageIndex = iconIndex;
treeNode.SelectedImageIndex = iconIndex;
}
}

The MockView:
public void SetIcon(string iconPath, string iconName)
{
return;
}


This works (have to rewrite some of the code in the SetIcon View
implementation though), and I can .Verify(...) the calls. I got green!

You might notice that ApplyIcons(path) in the Controller is marked as
private. It needs to be public to be tested, but the method will be
called once, and that is from the OnStartup() of the Controller.
Therefore I wouldn't mark it public, but since I'm trying to do unit
tests/mocking, would I still mark it public? If so, I imagine almost
all the methods of the Controller will be public?

A few more questions:
1. I already have 8 signatures in my interface and I'm far away from
completing the project, won't the interface grow way too big?
2. If an exception is thrown within the controller, would I catch it
there, log it and display an error message (if appropriate), or would
I let it "bubble up" to the View and log/display message there?
3. You said that methods shouldn't be static, why's that (just
interested)?
4. Are there any good books on this topic?

Hmm, I think I had more questions but my head went blank...

Once again, thanks for the help. I wouldn't have come this far without
you. :)

On Nov 1, 7:58 am, Emanuele DelBono <codiceplast...@gmail.com> wrote:
> Hi!
> Your code didn't scare me ;-)
>
> The direction is right but not yet completed.
> The methods in the controller should not work with concrete object of
> the UI (TreeView, ImageList, ...) because doing this way you can't
> easily test your controller and you are tied to the Winform
> implementation.
>
> Your controller get the IMainView interface in the constructor, and
> that right. Then it should use that interface to communicate with your
> view. For example to add the icons to the list:
>
> public void GetIcons(String iconPath)
> {
>   _mainView.SetImage(iconPath + @"\Administration.ico");
>    // ... others icons
>
> }
>
> In this way you can mock your view and verify that the SetImage method
> is called the right number of time with right values.
>
> [Test]
> public void SetIconTest()
> {
>   Mock<IMainView> view = new Mock<IMainView>();
>   Controller c = new Controller(view.Object);
>
>  c.GetIcons("iconPath");
>
>  view.Verify(v => v.SetImage(iconPath + @"\Administration.ico");
>
> }
>
> HTH, I know it's not easy, I had the same doubts when I started wiht TDD.
> A final note: don't use static methods, they give you problems in the tests.
>
> emahttp://blog.codiceplastico.com

Emanuele DelBono

unread,
Nov 3, 2009, 4:17:44 AM11/3/09
to moq...@googlegroups.com
On Mon, Nov 2, 2009 at 3:30 AM, Urhgan <e.ur...@gmail.com> wrote:
>
> Hello again,
Hi!
[....]

> You might notice that ApplyIcons(path) in the Controller is marked as
> private. It needs to be public to be tested, but the method will be
> called once, and that is from the OnStartup() of the Controller.
> Therefore I wouldn't mark it public, but since I'm trying to do unit
> tests/mocking, would I still mark it public? If so, I imagine almost
> all the methods of the Controller will be public?

To make a class testable sometimes you have to break the
incapsulation, so it's ok to make some methods public to make the
class more testable. Not all methods should be public, anyway.

>
> A few more questions:
> 1. I already have 8 signatures in my interface and I'm far away from
> completing the project, won't the interface grow way too big?

You should apply Single Responsibility Principle, is the class do too
much things, break it in 2 or 3 classes.

> 2. If an exception is thrown within the controller, would I catch it
> there, log it and display an error message (if appropriate), or would
> I let it "bubble up" to the View and log/display message there?

usualli is the controller that manage the errors, so it should catch
the exception and show an error message using the View (it's testable!
:-))

> 3. You said that methods shouldn't be static, why's that (just
> interested)?

Because the application became less testable, and it's not very
object-oriented..static is like global.


> 4. Are there any good books on this topic?

"The Art of unit testing" is a fantastic book:
http://www.amazon.com/Art-Unit-Testing-Examples-NET/dp/1933988274/ref=sr_1_1?ie=UTF8&s=books&qid=1257239809&sr=8-1


>
> Hmm, I think I had more questions but my head went blank...
>
> Once again, thanks for the help. I wouldn't have come this far without
> you. :)

You're welcome! ;-)

bye
ema

Reply all
Reply to author
Forward
0 new messages