Return and Generics?

664 views
Skip to first unread message

Luiz Angelo Heinzen

unread,
Dec 6, 2012, 2:04:43 PM12/6/12
to nsubs...@googlegroups.com
I'll struggling with a IView on a MVP project I'm developing. 

The View interface is:

    public interface IView : IBaseView
    {
        TextBox ClientId { get; set; }
        TextBox ClientName { get; set; }
        Button SaveClient { get; set; }
        Button NextClient { get; set; }
        Button PreviousClient { get; set; }
        Button DiscardChanges {get;set;}
        bool ReadOnly { get; set;  }
        ListBox MyLittleList { get; set; }
        C Get<C>(string controlName) where C : Control;
    }

The problem lies on the Get<C> method. The real implemenation is:

   public C Get<C>(string ControlName) where C : Control
        {
            var controlName = ControlName.ToLower();
            var underlyingControlName = controlName[0] + ControlName.Substring(1);
            var underlyingControl = this.Controls.Find(underlyingControlName, true).FirstOrDefault();
            return underlyingControl as C;
        }

How do I mock this easily?

I've tried:

            var view = Substitute.For<IView>();

            view.ClientId = new System.Windows.Forms.TextBox();
            view.ClientName = new System.Windows.Forms.TextBox();
            view.DiscardChanges = new System.Windows.Forms.Button();
            view.MyLittleList = new System.Windows.Forms.ListBox();
            view.NextClient = new System.Windows.Forms.Button();
            view.PreviousClient = new System.Windows.Forms.Button();
            view.ReadOnly = false;
            view.SaveClient = new System.Windows.Forms.Button();

            view.Get<TextBox>("ClientId").Returns(view.ClientId);
            view.Get<TextBox>("ClientName").Returns(view.ClientId);
            view.Get<Button>("DiscardChages").Returns(view.DiscardChanges);
            view.Get<ListBox>("DiscardChages").Returns(view.MyLittleList);
            view.Get<Button>("NextClient").Returns(view.NextClient);
            view.Get<Button>("PreviousClient").Returns(view.PreviousClient);
            view.Get<Button>("SaveClient").Returns(view.SaveClient);

But when I called view.Get<Button>("DiscardChanges"), it returns null instead of the view.DiscardChanges button. What I'm doing wrong?

This is used because I do "autowiring". The presenter checks the methods he declared and checks against the View. If there's a match (like a OnNextClient method on the presenter and a NextClient Button on the view) he auto wires it. 

        protected void wireEventsTo(IBaseView view)
        {
            Type presenterType = this.GetType();
            Type viewType = view.GetType();
            foreach (var method in presenterType.GetMethods())
            {
                var methodName = method.Name;
                if (methodName.StartsWith("On"))
                {
                    var presenterMethodName = methodName.Substring(2);
                    var matchingMember = viewType.GetMember(nameOfMemberToMatch).FirstOrDefault();

                    if (matchingMember == null) return;

                    if (matchingMember.MemberType == MemberTypes.Property)
                    {
                        wireMember(view, matchingMember, method);
                    }
                }
            }
        }
        private void wireMember(IBaseView view, MemberInfo match, MethodInfo method)
        {
            var matchingMemberType = ((PropertyInfo)match).PropertyType;

            if (matchingMemberType == typeof(Button))
            {
                var matchingButton = view.Get<Button>(match.Name);
                var eventHandler = (EventHandler)EventHandler.CreateDelegate(typeof(EventHandler), this, method);
                matchingButton.Click += eventHandler;
            }
        }

PS: I did try to mock the whole form but was granted the message: "DynamicProxy was unable to successfully replicate non-inheritable attribute System.Security.Permissions.UIPermissionAttribute".

Anyway, I'm almost giving up on the whole MVP. A lotta work. 

Xerxes Battiwalla

unread,
Dec 7, 2012, 5:29:37 AM12/7/12
to nsubs...@googlegroups.com
Does the code work in your app? Perhaps your test is set up incorrectly and the value in match.Name isn't coming back as "DiscardChanges". Is that MemberInfo match coming from another substitute? It's hard to say because it's coming from nameOfMemberToMatch defined elsewhere 


FWIW I just tried this and it worked fine...I'd hazard a guess that you're missing wire-up somewhere of a substitute return value.

public interface IFoo
{
    T GetThing<T>(string name) where T: Control;
}

void Main()
{
    var s = Substitute.For<IFoo>();
    s.GetThing<Button>("Hello");
    s.Received().GetThing<Button>("Hello");
}


-xerx



--
You received this message because you are subscribed to the Google Groups "NSubstitute" group.
To view this discussion on the web visit https://groups.google.com/d/msg/nsubstitute/-/OLvY6hemJsIJ.
To post to this group, send email to nsubs...@googlegroups.com.
To unsubscribe from this group, send email to nsubstitute...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nsubstitute?hl=en.

Luiz Angelo Heinzen

unread,
Dec 7, 2012, 7:02:26 AM12/7/12
to nsubs...@googlegroups.com
It works fine in my app. 

I thought it was because I've had set too much .Returns, but I've tested setting one and calling in the next line and it was still returning null.

But since I've posted I changed my approach, trying go to the DRY way. 

I have two helper methods. One for initializing the members of the interface.

        public static void SetData<T>(T obj)
        {
            foreach (var property in typeof(T).GetProperties())
                if (property.CanWrite && property.GetIndexParameters().Length == 0)
                {
                    object val = null;
                    val = Activator.CreateInstance(property.PropertyType);
                    property.SetValue(obj, val, null);
                }
        }

So now I just need to call 

    var view = Substitute.For<IView>();
    SetData<IView>(view);

Instead of setting manually. 

And I've turned the GetControl into a HelperClass set at runtime. I

    public class GetIVIewControlHelper : ControlHelper
    {
        public override C Get<C>(object container, string controlName)
        {
            var containerType = container.GetType();
            foreach (var property in container.GetType().GetProperties())
            {
                if (property.Name == controlName)
                {
                    return property.GetValue(container,null) as C;
                }
            }
            return null;
        }
    }

    public class GetFormControlsHelper : ControlHelper
    {
        public override C Get<C>(object container, string controlNameParam)
        {
            var form = (Form)container;

            var controlName = controlNameParam.ToLower();
            var underlyingControlName = controlName[0] + controlNameParam.Substring(1);
            var underlyingControl = form.Controls.Find(underlyingControlName, true).FirstOrDefault();
            return underlyingControl as C;
        }
    }

Since it's called in the presenter, I don't even need to set it manually. 

Anyway, thank you for your help!
Reply all
Reply to author
Forward
0 new messages