Skip to main content
Rollback to Revision 1
Source Link
syb0rg
  • 21.8k
  • 10
  • 112
  • 191

Edit. What I have come up with is the following test method.

[TestClass]
public class DocumentManagementTests
{

    private Mock<IProgress<string>> mockProgress;
    private Mock<IMessageBoxService> mockMessageBoxService;

    private int messageBoxInvocationCounter = 0;
    private List<string> filePaths = new List<string>();

    [TestInitialize]
    public void Initialize()
    {
        // Progress mock.
        mockProgress = new Mock<IProgress<string>>();
        mockProgress.Setup(m => m.Report(It.IsAny<string>()))
             .Callback((string s) =>
             {
                 Trace.WriteLine(s);
             });
        Assert.IsNotNull(mockProgress);

        // Load the file paths.
        LoadTextDocumentFilePaths();
    }

    private void LoadTextDocumentFilePaths()
    {
        string path = String.Empty;
        string directory = Utils.GetAssemblyDirectory();
        foreach (var fileName in new List<string>() { "DocumentA.txt", "DocumentB.txt" })
        {
            path = Path.Combine(directory, "Resources", "TextEditorDocs", fileName);
            if (!File.Exists(path))
                throw new IOException($"{path} does not exist");

            filePaths.Add(path);
        }
        Assert.AreEqual(2, filePaths.Count);
    }

    [TestMethod]
    public void DocumentChangedSavePrompt_OnUserClosing_Test()
    {
        // Message box service mock.
        mockMessageBoxService = new Mock<IMessageBoxService>();
        mockMessageBoxService
            .Setup(m => m.DisplayMessage(It.IsAny<IWin32Window>(), It.IsAny<string>(), It.IsAny<string>(),
                It.IsAny<MessageBoxButtons>(), It.IsAny<MessageBoxIcon>(), It.IsAny<MessageBoxDefaultButton>()))
            .Returns((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) => DialogResult.Cancel)
            .Callback((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) =>
            {
                Trace.WriteLine("MockMessageBoxService Invoked");
                messageBoxInvocationCounter++;
            });
        Assert.IsNotNull(mockMessageBoxService);

        // Open the documents.
        List<TextEditorController> controllers = new List<TextEditorController>();
        foreach (var path in filePaths)
        {
            TextEditorController controller = new TextEditorController(
                CompositionRoot.Resolve<ITextEditorView>(),
                mockMessageBoxService.Object);
            Assert.IsNotNull(controller);

            controllers.Add(controller);
            TextEditorView view = (TextEditorView)controller.Open(path);

            view.TextEditor.Text += "*";
            Assert.IsTrue(controller.IsDirty);
        }

        // Test they are dirty and the message box is displayed.
        foreach (var controller in controllers)
        {
            bool didClose = controller.TryClose(
                new FormClosingEventArgs(CloseReason.UserClosing, false));
            Assert.IsFalse(didClose);
        }
        Assert.AreEqual(2, messageBoxInvocationCounter);
    }
}

Edit. What I have come up with is the following test method.

[TestClass]
public class DocumentManagementTests
{

    private Mock<IProgress<string>> mockProgress;
    private Mock<IMessageBoxService> mockMessageBoxService;

    private int messageBoxInvocationCounter = 0;
    private List<string> filePaths = new List<string>();

    [TestInitialize]
    public void Initialize()
    {
        // Progress mock.
        mockProgress = new Mock<IProgress<string>>();
        mockProgress.Setup(m => m.Report(It.IsAny<string>()))
             .Callback((string s) =>
             {
                 Trace.WriteLine(s);
             });
        Assert.IsNotNull(mockProgress);

        // Load the file paths.
        LoadTextDocumentFilePaths();
    }

    private void LoadTextDocumentFilePaths()
    {
        string path = String.Empty;
        string directory = Utils.GetAssemblyDirectory();
        foreach (var fileName in new List<string>() { "DocumentA.txt", "DocumentB.txt" })
        {
            path = Path.Combine(directory, "Resources", "TextEditorDocs", fileName);
            if (!File.Exists(path))
                throw new IOException($"{path} does not exist");

            filePaths.Add(path);
        }
        Assert.AreEqual(2, filePaths.Count);
    }

    [TestMethod]
    public void DocumentChangedSavePrompt_OnUserClosing_Test()
    {
        // Message box service mock.
        mockMessageBoxService = new Mock<IMessageBoxService>();
        mockMessageBoxService
            .Setup(m => m.DisplayMessage(It.IsAny<IWin32Window>(), It.IsAny<string>(), It.IsAny<string>(),
                It.IsAny<MessageBoxButtons>(), It.IsAny<MessageBoxIcon>(), It.IsAny<MessageBoxDefaultButton>()))
            .Returns((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) => DialogResult.Cancel)
            .Callback((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) =>
            {
                Trace.WriteLine("MockMessageBoxService Invoked");
                messageBoxInvocationCounter++;
            });
        Assert.IsNotNull(mockMessageBoxService);

        // Open the documents.
        List<TextEditorController> controllers = new List<TextEditorController>();
        foreach (var path in filePaths)
        {
            TextEditorController controller = new TextEditorController(
                CompositionRoot.Resolve<ITextEditorView>(),
                mockMessageBoxService.Object);
            Assert.IsNotNull(controller);

            controllers.Add(controller);
            TextEditorView view = (TextEditorView)controller.Open(path);

            view.TextEditor.Text += "*";
            Assert.IsTrue(controller.IsDirty);
        }

        // Test they are dirty and the message box is displayed.
        foreach (var controller in controllers)
        {
            bool didClose = controller.TryClose(
                new FormClosingEventArgs(CloseReason.UserClosing, false));
            Assert.IsFalse(didClose);
        }
        Assert.AreEqual(2, messageBoxInvocationCounter);
    }
}
added 3681 characters in body
Source Link
MoonKnight
  • 829
  • 2
  • 11
  • 17

Edit. What I have come up with is the following test method.

[TestClass]
public class DocumentManagementTests
{

    private Mock<IProgress<string>> mockProgress;
    private Mock<IMessageBoxService> mockMessageBoxService;

    private int messageBoxInvocationCounter = 0;
    private List<string> filePaths = new List<string>();

    [TestInitialize]
    public void Initialize()
    {
        // Progress mock.
        mockProgress = new Mock<IProgress<string>>();
        mockProgress.Setup(m => m.Report(It.IsAny<string>()))
             .Callback((string s) =>
             {
                 Trace.WriteLine(s);
             });
        Assert.IsNotNull(mockProgress);

        // Load the file paths.
        LoadTextDocumentFilePaths();
    }

    private void LoadTextDocumentFilePaths()
    {
        string path = String.Empty;
        string directory = Utils.GetAssemblyDirectory();
        foreach (var fileName in new List<string>() { "DocumentA.txt", "DocumentB.txt" })
        {
            path = Path.Combine(directory, "Resources", "TextEditorDocs", fileName);
            if (!File.Exists(path))
                throw new IOException($"{path} does not exist");

            filePaths.Add(path);
        }
        Assert.AreEqual(2, filePaths.Count);
    }

    [TestMethod]
    public void DocumentChangedSavePrompt_OnUserClosing_Test()
    {
        // Message box service mock.
        mockMessageBoxService = new Mock<IMessageBoxService>();
        mockMessageBoxService
            .Setup(m => m.DisplayMessage(It.IsAny<IWin32Window>(), It.IsAny<string>(), It.IsAny<string>(),
                It.IsAny<MessageBoxButtons>(), It.IsAny<MessageBoxIcon>(), It.IsAny<MessageBoxDefaultButton>()))
            .Returns((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) => DialogResult.Cancel)
            .Callback((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) =>
            {
                Trace.WriteLine("MockMessageBoxService Invoked");
                messageBoxInvocationCounter++;
            });
        Assert.IsNotNull(mockMessageBoxService);

        // Open the documents.
        List<TextEditorController> controllers = new List<TextEditorController>();
        foreach (var path in filePaths)
        {
            TextEditorController controller = new TextEditorController(
                CompositionRoot.Resolve<ITextEditorView>(),
                mockMessageBoxService.Object);
            Assert.IsNotNull(controller);

            controllers.Add(controller);
            TextEditorView view = (TextEditorView)controller.Open(path);

            view.TextEditor.Text += "*";
            Assert.IsTrue(controller.IsDirty);
        }

        // Test they are dirty and the message box is displayed.
        foreach (var controller in controllers)
        {
            bool didClose = controller.TryClose(
                new FormClosingEventArgs(CloseReason.UserClosing, false));
            Assert.IsFalse(didClose);
        }
        Assert.AreEqual(2, messageBoxInvocationCounter);
    }
}

Edit. What I have come up with is the following test method.

[TestClass]
public class DocumentManagementTests
{

    private Mock<IProgress<string>> mockProgress;
    private Mock<IMessageBoxService> mockMessageBoxService;

    private int messageBoxInvocationCounter = 0;
    private List<string> filePaths = new List<string>();

    [TestInitialize]
    public void Initialize()
    {
        // Progress mock.
        mockProgress = new Mock<IProgress<string>>();
        mockProgress.Setup(m => m.Report(It.IsAny<string>()))
             .Callback((string s) =>
             {
                 Trace.WriteLine(s);
             });
        Assert.IsNotNull(mockProgress);

        // Load the file paths.
        LoadTextDocumentFilePaths();
    }

    private void LoadTextDocumentFilePaths()
    {
        string path = String.Empty;
        string directory = Utils.GetAssemblyDirectory();
        foreach (var fileName in new List<string>() { "DocumentA.txt", "DocumentB.txt" })
        {
            path = Path.Combine(directory, "Resources", "TextEditorDocs", fileName);
            if (!File.Exists(path))
                throw new IOException($"{path} does not exist");

            filePaths.Add(path);
        }
        Assert.AreEqual(2, filePaths.Count);
    }

    [TestMethod]
    public void DocumentChangedSavePrompt_OnUserClosing_Test()
    {
        // Message box service mock.
        mockMessageBoxService = new Mock<IMessageBoxService>();
        mockMessageBoxService
            .Setup(m => m.DisplayMessage(It.IsAny<IWin32Window>(), It.IsAny<string>(), It.IsAny<string>(),
                It.IsAny<MessageBoxButtons>(), It.IsAny<MessageBoxIcon>(), It.IsAny<MessageBoxDefaultButton>()))
            .Returns((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) => DialogResult.Cancel)
            .Callback((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) =>
            {
                Trace.WriteLine("MockMessageBoxService Invoked");
                messageBoxInvocationCounter++;
            });
        Assert.IsNotNull(mockMessageBoxService);

        // Open the documents.
        List<TextEditorController> controllers = new List<TextEditorController>();
        foreach (var path in filePaths)
        {
            TextEditorController controller = new TextEditorController(
                CompositionRoot.Resolve<ITextEditorView>(),
                mockMessageBoxService.Object);
            Assert.IsNotNull(controller);

            controllers.Add(controller);
            TextEditorView view = (TextEditorView)controller.Open(path);

            view.TextEditor.Text += "*";
            Assert.IsTrue(controller.IsDirty);
        }

        // Test they are dirty and the message box is displayed.
        foreach (var controller in controllers)
        {
            bool didClose = controller.TryClose(
                new FormClosingEventArgs(CloseReason.UserClosing, false));
            Assert.IsFalse(didClose);
        }
        Assert.AreEqual(2, messageBoxInvocationCounter);
    }
}
Source Link
MoonKnight
  • 829
  • 2
  • 11
  • 17

UnitTesting with Mocking and Dependency Injection using Ninject

I have the following scenario that I have not come across so far using Ninject. I have the following class structure (simplified for easy of reading :]). First the abstract base class for all IDocuments

public abstract class DocumentController : IDocumentController, IDisposable
{
    ...
    private IMessageBoxService messageBoxService;
    private IUndoRedoManager undoRedoManager;
    private FileSystemObserver observer;
    private bool fileChanging;

    public DocumentController(IMessageBoxService messageBoxService)
    {
        if (messageBoxService == null)
            throw new ArgumentNullException("messageBoxService");

        this.messageBoxService = messageBoxService;
    }
    ...
    private void FileChangedHandler(string p)
    {
        if (!fileChanging && p.CompareNoCase(FilePath))
        {
            fileChanging = true;
            try
            {
                (View as Form).UIThread(() =>
                {
                    DialogResult result = messageBoxService.DisplayMessage(
                        (Form)View,
                        String.Format(
                            MessageStrings.DocumentController_DocumentChanged,
                            FilePath,
                            Constants.Trademark),
                        CaptionStrings.ChangeNotification,
                        MessageBoxButtons.YesNo, MessageBoxIcon.Information);

                    if (result == DialogResult.Yes)
                    {
                        if (TryClose(new FormClosingEventArgs(CloseReason.None, false)))
                        {
                            MediatorService.Instance.NotifyColleagues(
                                MediatorService.Messages.OpenDocuments,
                                new List<string>() { FilePath });
                            Dispose();
                        }
                    }
                });
            }
            finally
            {
                fileChanging = false;
            }
        }
    }
    // Omitted code for not saved prompt for brevity.
}

This class watches all documents that are open and if they are changed in the file system shows a prompt. If a close is attempted whilst they are in a "dirty" state, the same IMessageBoxService prompts to save.

An example of the derived concrete class ctors is

public class TextEditorController : DocumentController, IZoom
{
    ...
    public TextEditorController(ITextEditorView view, IMessageBoxService messageBoxService) 
        : base(messageBoxService)
    {
        if (view == null)
            throw new ArgumentNullException("view");

        this.view = view;
        InitializeEventHandlers();
    }
    ...
}

Now this works great when using the application. The problem I have is with unit testing. What I want to do is write a unit test that opens my text documents, makes some changes and then trys to close them when they are "dirty"/unsaved. This will show a message box, but I want to Mock the IMessageBoxService and test whether the message box is indeed displayed under the right conditions. I have the following unit test class in which I have reverted to instantiating concrete implementations of things; it works but feels bad...

[TestClass]
public class DocumentManagementTests
{
    private Mock<IProgress<string>> mockProgress;
    private Mock<IMessageBoxService> mockMessageBoxService;

    private int messageBoxInvocationCounter = 0;
    private List<string> filePaths = new List<string>();

    [TestInitialize]
    public void Initialize()
    {
        mockProgress = new Mock<IProgress<string>>();
        mockProgress.Setup(m => m.Report(It.IsAny<string>()))
             .Callback((string s) =>
             {
                 Trace.WriteLine(s);
             });
        Assert.IsNotNull(mockProgress);

        // Load the file paths.
        LoadTextDocumentFilePaths();
    }

    private void LoadTextDocumentFilePaths()
    {
        string path = String.Empty;
        string directory = Utils.GetAssemblyDirectory();
        foreach (var fileName in new List<string>() { "DocumentA.txt", "DocumentB.txt" })
        {
            path = Path.Combine(directory, "Resources", "TextEditorDocs", fileName);
            if (!File.Exists(path))
                throw new IOException($"{path} does not exist");

            filePaths.Add(path);
        }
        Assert.AreEqual(2, filePaths.Count);
    }

    [TestMethod]
    public void DocumentChangedSavePrompt_OnUserClosing_Test()
    {
        // Message box service mock.
        mockMessageBoxService = new Mock<IMessageBoxService>();
        mockMessageBoxService
            .Setup(m => m.DisplayMessage(It.IsAny<IWin32Window>(), It.IsAny<string>(), It.IsAny<string>(),
                It.IsAny<MessageBoxButtons>(), It.IsAny<MessageBoxIcon>(), It.IsAny<MessageBoxDefaultButton>()))
            .Returns((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) => DialogResult.Cancel)
            .Callback((IWin32Window owner, string text,
                string caption, MessageBoxButtons buttons, MessageBoxIcon icon,
                MessageBoxDefaultButton defaultButton) =>
            {
                Trace.WriteLine("MockMessageBoxService Invoked");
                messageBoxInvocationCounter++;
            });
        Assert.IsNotNull(mockMessageBoxService);

        // Open the documents.
        List<TextEditorController> controllers = new List<TextEditorController>();
        foreach (var path in filePaths)
        {
            // CONCRETE IMPLEMENTATIONS - BAD!??
            TextEditorController controller = new TextEditorController(
                CompositionRoot.Resolve<ITextEditorView>(),
                mockMessageBoxService.Object);
            Assert.IsNotNull(controller);

            controllers.Add(controller);
            TextEditorView view = (TextEditorView)controller.Open(path);

            view.TextEditor.Text += "*";
            Assert.IsTrue(controller.IsDirty);
        }

        // Test they are dirty and the message box is displayed.
        foreach (var controller in controllers)
        {
            bool didClose = controller.TryClose(
                new FormClosingEventArgs(CloseReason.UserClosing, false));
            Assert.IsFalse(didClose);
        }
        Assert.AreEqual(2, messageBoxInvocationCounter);
    }
}

I want to do something like this

[TestClass]
public class DocumentManagementTests
{
    private IDocumentProvider documentProvider;
    private Moq.Mock<IProgress<string>> mockProgress;
    private List<string> filePaths = new List<string>();

    #region Initialization.
    [TestInitialize]
    public void Initialize()
    {
        documentProvider = CompositionRoot.Resolve<IDocumentProvider>();
        if (documentProvider == null)
            throw new ArgumentNullException("documentManager");

        mockProgress = new Mock<IProgress<string>>();
        mockProgress.Setup(m => m.Report(It.IsAny<string>()))
             .Callback((string s) =>
             {
                 Trace.WriteLine(s);
             });

        LoadTextDocumentFilePaths();
    }

    private void LoadTextDocumentFilePaths()
    {
        // Add two real file paths to filePaths.
        Assert.AreEqual(2, filePaths.Count);
    }

    [TestMethod]
    public void DocumentChangedSavePromptTest()
    {
        // HOW DO I INJECT A MOCKED IMessageBoxService to `IDocumentController`?
    
        // Open the documents internally.
        var documents = documentProvider.GetDocumentViews(filePaths, mockProgress.Object);
        Assert.AreEqual(2, documentProvider.DocumentControllerCache
            .Count(d => d.GetType() == typeof(TextEditorController)));

        // Ammend the text internally. 
        foreach (var d in documentProvider.DocumentControllerCache)
        {
            var controller = d.Key as TextEditorController;

        }
        // Need to finish writing the test.
    }
}

So I want to know:

  1. How I can inject a mocked IMessageBoxService into the ctor for the IDocumentController class?
  2. Is the above is not possible, is there a way I can use DI in my test, if so how?
  3. Is what I have done reasonable in the worst case scenario?

Thanks for your time.