如何Moq这个观点?

时间:2014-03-26 18:29:10

标签: c# winforms unit-testing moq mvp

我有一个视图,我想嘲笑Show行为。

Authentication dialog

输入凭据后,[Connecter]按钮自动启用,然后用户可以单击。我希望我可以重现这种行为,而不必显示视图,实际上真的输入我的凭据。

该应用程序是IApplicationPresenter提供的WinForms MDI。 IApplicationPresenter会引发ShowView订阅的IApplicationView

然后,当IApplicationView.Shown时,IApplicationPresenter强制用户进行身份验证,如下所示:

IApplicationPresenter.OnViewShown

public void OnViewShown() { forceAuthentication(); }

private void forceAuthentication() {
    IAuthenticationView authView = new AuthenticationView();
    IAuthenticationPrenseter authPresenter = new AuthenticationPresenter();
    authPresenter.ShowView();
}

就像我闻到一样。

  1. 就像我可以将IAuthenticationView注入IApplicationPresenter。然后,这将允许我向其注入我的模拟视图,并避免实际显示视图,这实际上是我想要的。它是制作它的最好方法吗?
  2. 现在,我想测试一下,当显示IApplicationView时,会通知IApplicationPresenter并强制进行身份验证。

    在这里嘲笑方面有什么更好的方法?

      

    更新

    IVIEW

    public interface IView {
        void CloseView();
        void SetTitle(string title);
        void ShowView();
        void RaiseVoidEvent(VoidEventHandler @event);
    
        event VoidEventHandler OnViewInitialize;
        event VoidEventHandler OnViewShown;
    }
    

    IApplicationView

    public interface IApplicationView : IView {
        void OnUserAuthenticated();
    
        event VoidEventHandler ManageRequestsClicked;
    }
    

    IPresenter

    public interface IPresenter<V> where V : IView {
        V View { get; }
        IDatabaseUser CurrentUser { get; }
    
        void CloseView();
        void OnViewInitialize();
        void RaiseVoidEvent(VoidEventHandler @event);
        void ShowView();
    
        event VoidEventHandler OnCloseView;
        event VoidEventHandler OnShowView;
    }
    

    演示

    public abstract class Presenter<V> : IPresenter<V> where V : IView {
        public Presenter(V view) {
            if (view == null) throw new ArgumentNullException("view");
    
            View = view;
            View.OnViewInitialize += OnViewInitialize;
    
            OnCloseView += View.CloseView;            
            OnShowView += View.ShowView;
        }
    
        public virtual IDatabaseUser CurrentUser { get; protected set; }
        public virtual V View { get; private set; }
    
        public virtual void CloseView() { RaiseVoidEvent(OnCloseView); }
        public virtual void OnViewInitialize() { }
        public void RaiseVoidEvent(VoidEventHandler @event) { if (@event != null) @event(); }
        public virtual void ShowView() { RaiseVoidEvent(OnShowView); }
    
        public virtual event VoidEventHandler OnCloseView;
        public virtual event VoidEventHandler OnShowView;
    }
    

    IApplicationPresenter

    public interface IApplicationPresenter : IPresenter<IApplicationView> {
        IAuthenticationPresenter AuthenticationPresenter { get; set; }
    
        void OnManageRequestsClicked();
        void OnUserAuthenticated(UserAuthenticatedEventArgs e);
        void OnViewShown();
    }
    

    ApplicationPresenter

    public class ApplicationPresenter : Presenter<IApplicationView>, IApplicationPresenter {
        public ApplicationPresenter(IApplicationView view) : this(view, null) { }
        public ApplicationPresenter(IApplicationView view, IAuthenticationPresenter authPresenter) : base(view) {
            AuthenticationPresenter = authPresenter;            
            View.OnViewShown += OnViewShown;
            View.ManageRequestsClicked += OnManageRequestsClicked;
        }
    
        public IAuthenticationPresenter AuthenticationPresenter { get { return authenticationPresenter; } set { setAuthenticationPresenter(value); } }
    
        public void OnManageRequestsClicked() {
            var requests = new GestionDemandeAccesInformationForm();
            requests.Database = database;
            requests.MdiParent = (System.Windows.Forms.Form)View;
            requests.Show();
        }
    
        public void OnUserAuthenticated(UserAuthenticatedEventArgs e) { 
            CurrentUser = new DatabaseUser(e.Login, e.Password, e.DatabaseInstance);
            database = new DatabaseSessionFactory(CurrentUser);
            setAppTitle();
            showRequestsManagementView();
        }
    
        public void OnViewShown() { forceAuthentication(); }
    }
    

    IAuthenticationView

    public interface IAuthenticationView : IView {
        string ErrorMessage { get; set; }
        string Instance { get; set; }
        IEnumerable<string> Instances { get; set; }
        string Login { get; set; }
        string Password { get; set; }
    
        void EnableConnectButton(bool enabled);
        void SetDefaultInstance(string defaultInstance);
        void RaiseSelectionChangedEvent(SelectionChangedEventHandler @event, SelectionChangedEventArgs e);
    
        event VoidEventHandler OnConnect;
        event SelectionChangedEventHandler OnDatabaseInstanceChanged;
        event VoidEventHandler OnLoginChanged;
        event VoidEventHandler OnPasswordChanged;
    }
    

    IAuthenticationPresenter

    public interface IAuthenticationPresenter : IValidatablePresenter, IPresenter<IAuthenticationView> {
        void OnConnect();
        void OnViewDatabaseInstanceChanged(SelectionChangedEventArgs e);
        void OnViewLoginChanged();
        void OnViewPasswordChanged();
        void RaiseUserAuthenticatedEvent(UserAuthenticatedEventArgs e);
        event UserAuthenticatedEventHandler UserAuthenticated;
    }
    

    AuthenticationPresenter

    public class AuthenticationPresenter : Presenter<IAuthenticationView>, IAuthenticationPresenter {
        public AuthenticationPresenter(IAuthenticationView view, IMembershipService service) : base(view) {
            MembershipService = service;
            View.ErrorMessage = null;
            View.SetTitle(ViewTitle);
            subscribeToEvents();
        }
    
        public bool IsValid { get { return credentialsEntered(); } }
        public IMembershipService MembershipService { get; set; }
    
        public virtual void OnConnect() {
            if (noDatabaseInstanceSelected()) display(MissingInstanceErrorMessage);
            else if (noLoginEntered()) display(MissingLoginErrorMessage);
            else if (noPasswordEntered()) display(MissingPasswordErrorMessage);
            else {
                display(EverythingIsFine);
                if (isAuthenticUser()) notifyTheApplicationThatTheUserIsAuthentic();
                else { display(InvalidLoginOrPasswordErrorMessage); }
            }
        }
    
        public override void OnViewInitialize() {
            base.OnViewInitialize();
            View.ErrorMessage = null;
            View.Instances = Configuration.DatabaseInstances;
            View.SetDefaultInstance(Configuration.DefaultInstance);
        }
    
        public void OnViewDatabaseInstanceChanged(SelectionChangedEventArgs e) { View.Instance = (string)e.Selected; }
        public void OnViewLoginChanged() { View.EnableConnectButton(IsValid); }
        public void OnViewPasswordChanged() { View.EnableConnectButton(IsValid); }
        public void RaiseUserAuthenticatedEvent(UserAuthenticatedEventArgs e) { if (UserAuthenticated != null) UserAuthenticated(e); }
    
        public event UserAuthenticatedEventHandler UserAuthenticated;
    }
    

2 个答案:

答案 0 :(得分:2)

如果我是你,我会注入一个工厂来创建AuthenticationPresenter,在你的测试中我会打电话给OnViewShown()并在你的模拟器(工厂退回的演示者)上验证ShowView被称为。{/ p>

修改 请注意,我还没有编译这个,我现在没有C#编译器。

这是我的测试版本。基于我对你真正想要测试的内容的解释:

[TestClass]
public class ApplicationPresenterTests 
{
    [TestClass]
    public class OnViewShown : ApplicationPresenterTests 
    {
        [TestMethod]
        public void ForceAuthentication() 
        {
            // given
            var authenticationPresenterFactory = new Mock<IAuthenticationPresenterFactory>();
            var authenticationPresenter = new Mock<IAuthenticationPresenter>();
            authenticationPresenterFactory.Setup(f => f.create()).Returns(authenticationPresenter.Object);
            var presenter = new ApplicationPresenter(authenticationPresenterFactory);

            // when
            presenter.OnViewShown();

            // then
            authenticationPresenter.Verify(p => p.ShowView());
        }
}

答案 1 :(得分:0)

到目前为止,我已经提出了完美无瑕的解决方案。

所有关于设置模拟对象按预期工作的方法。

[TestClass]
public abstract class ApplicationPresenterTests {
    [TestClass]
    public class OnViewShown : ApplicationPresenterTests {
        [TestMethod]
        public void ForceAuthentication() {
            // arrange

            // act
            Presenter.OnViewShown();
            var actual = Presenter.CurrentUser;

            // assert
            Assert.IsNotNull(actual);
            Assert.IsInstanceOfType(actual, typeof(IDatabaseUser));
        }
    }

    [TestInitialize]
    public void ApplicationMainPresenterSetUp() {
        Mock<IAuthenticationView> authView = new Mock<IAuthenticationView>(MockBehavior.Strict);
        authView.SetupProperty(v => v.ErrorMessage);
        authView.SetupGet(v => v.Instance).Returns(RandomValues.RandomString());
        authView.SetupGet(v => v.Login).Returns(RandomValues.RandomString());
        authView.SetupGet(v => v.Password).Returns(RandomValues.RandomString());
        authView.Setup(v => v.CloseView());
        authView.Setup(v => v.SetTitle(It.IsAny<string>()));
        authView.Setup(v => v.ShowView()).Raises(v => v.OnConnect += null).Verifiable();

        Mock<IMembershipService> authService = new Mock<IMembershipService>(MockBehavior.Loose);
        authService.Setup(s => s.AuthenticateUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(true);

        IAuthenticationPresenter authPresenter = new AuthenticationPresenter(authView.Object, authService.Object);

        ApplicationView = new ApplicationForm();

        Presenter = new ApplicationPresenter(ApplicationView, authPresenter);
    }

    protected IApplicationView ApplicationView { get; private set; }
    protected IApplicationPresenter Presenter { get; private set; }
}

因此,关键的变化是将IAuthenticationPresenter的依赖性注入IApplicationPresenter,因此ApplicationPresenter构造函数重载。

虽然这已经解决了我的问题,但我更好地理解了PresenterFactory注入ApplicationPresenter的必要性,因为这是处理应用程序中所有内容的演示者,即调用每个有自己的演示者的其他视图。

在我之前,面临更复杂的挑战。请继续关注!