From d8f65b8690de2458e6673cae7aec067e409dbd55 Mon Sep 17 00:00:00 2001 From: Luca Auer Date: Tue, 12 Dec 2023 18:22:15 +0100 Subject: [PATCH 1/3] Add NSubstitute --- tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj b/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj index db042f2b..92955f0d 100644 --- a/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj +++ b/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj @@ -11,6 +11,11 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 8ae23a9f4e41ee2b6b673ab7c6a1062faa591716 Mon Sep 17 00:00:00 2001 From: Luca Auer Date: Tue, 12 Dec 2023 18:58:19 +0100 Subject: [PATCH 2/3] Replace all uses of Moq --- .../Actions/DeleteActionTests.cs | 34 ++++----- .../Actions/MoveActionTests.cs | 34 ++++----- .../Actions/RenameActionTests.cs | 34 ++++----- .../ViewModels/ActionsViewModelTest.cs | 38 +++++----- .../FolderTreeItemViewModelTests.cs | 68 +++++++---------- .../ViewModels/FoldersViewModelTests.cs | 65 ++++------------ .../ViewModels/ImagesViewModelTests.cs | 40 +++++----- .../ViewModels/MainViewModelTests.cs | 75 +++++++++---------- .../ViewModels/MetadataViewModelTests.cs | 66 +++++++--------- 9 files changed, 189 insertions(+), 265 deletions(-) diff --git a/tests/ImageSort.UnitTests/Actions/DeleteActionTests.cs b/tests/ImageSort.UnitTests/Actions/DeleteActionTests.cs index 34cd5182..bad940af 100644 --- a/tests/ImageSort.UnitTests/Actions/DeleteActionTests.cs +++ b/tests/ImageSort.UnitTests/Actions/DeleteActionTests.cs @@ -2,7 +2,7 @@ using System.IO; using ImageSort.Actions; using ImageSort.FileSystem; -using Moq; +using NSubstitute; using Xunit; namespace ImageSort.UnitTests.Actions; @@ -14,28 +14,25 @@ public void DeletesAndRestoresTheFileCorrectly() { const string fileToDelete = @"C:\Some File.png"; - var fsMock = new Mock(); - var recycleBinMock = new Mock(); - var fileRestorerMock = new Mock(); + var fsMock = Substitute.For(); + var recycleBinMock = Substitute.For(); + var fileRestorerMock = Substitute.For(); - fsMock.Setup(fs => fs.FileExists(fileToDelete)).Returns(true).Verifiable(); + fsMock.FileExists(fileToDelete).Returns(true); - fileRestorerMock.Setup(fr => fr.Dispose()).Verifiable(); + recycleBinMock.Send(fileToDelete, false).Returns(fileRestorerMock); - recycleBinMock.Setup(recycleBin => recycleBin.Send(fileToDelete, false)).Returns(fileRestorerMock.Object) - .Verifiable(); + var deleteAction = new DeleteAction(fileToDelete, fsMock, recycleBinMock); - var deleteAction = new DeleteAction(fileToDelete, fsMock.Object, recycleBinMock.Object); - - fsMock.Verify(fs => fs.FileExists(fileToDelete)); + fsMock.Received().FileExists(fileToDelete); deleteAction.Act(); - recycleBinMock.Verify(recycleBin => recycleBin.Send(fileToDelete, false)); + recycleBinMock.Received().Send(fileToDelete, false); deleteAction.Revert(); - fileRestorerMock.Verify(fr => fr.Dispose()); + fileRestorerMock.Received().Dispose(); } [Fact(DisplayName = "Throws when the file to delete does not exist")] @@ -43,15 +40,14 @@ public void ThrowsWhenTheFileDoesNotExist() { const string fileThatDoesntExist = @"C:\Fictional File.fake"; - var fsMock = new Mock(); - var recycleBinMock = new Mock(); - var fileRestorerMock = new Mock(); + var fsMock = Substitute.For(); + var recycleBinMock = Substitute.For(); - fsMock.Setup(fs => fs.FileExists(fileThatDoesntExist)).Returns(false).Verifiable(); + fsMock.FileExists(fileThatDoesntExist).Returns(false); Assert.Throws(() => - new DeleteAction(fileThatDoesntExist, fsMock.Object, recycleBinMock.Object)); + new DeleteAction(fileThatDoesntExist, fsMock, recycleBinMock)); - fsMock.Verify(fs => fs.FileExists(fileThatDoesntExist)); + fsMock.Received().FileExists(fileThatDoesntExist); } } \ No newline at end of file diff --git a/tests/ImageSort.UnitTests/Actions/MoveActionTests.cs b/tests/ImageSort.UnitTests/Actions/MoveActionTests.cs index 64e5efaf..129e2d9a 100644 --- a/tests/ImageSort.UnitTests/Actions/MoveActionTests.cs +++ b/tests/ImageSort.UnitTests/Actions/MoveActionTests.cs @@ -1,7 +1,7 @@ using System.IO; using ImageSort.Actions; using ImageSort.FileSystem; -using Moq; +using NSubstitute; using Xunit; namespace ImageSort.UnitTests.Actions; @@ -18,27 +18,25 @@ public void FileGetsMovedCorrectly() var notifedOfAction = false; var notifiedOfReversion = false; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.Move(oldPath, newPath)).Verifiable(); - fsMock.Setup(fs => fs.Move(newPath, oldPath)).Verifiable(); - fsMock.Setup(fs => fs.FileExists(oldPath)).Returns(true); - fsMock.Setup(fs => fs.DirectoryExists(newFolder)).Returns(true); + fsMock.FileExists(oldPath).Returns(true); + fsMock.DirectoryExists(newFolder).Returns(true); - var moveAction = new MoveAction(oldPath, newFolder, fsMock.Object, + var moveAction = new MoveAction(oldPath, newFolder, fsMock, (f, t) => notifedOfAction = true, (f, t) => notifiedOfReversion = true); - fsMock.Verify(fs => fs.FileExists(oldPath)); - fsMock.Verify(fs => fs.DirectoryExists(newFolder)); + fsMock.Received().FileExists(oldPath); + fsMock.Received().DirectoryExists(newFolder); moveAction.Act(); - fsMock.Verify(fs => fs.Move(oldPath, newPath)); + fsMock.Received().Move(oldPath, newPath); moveAction.Revert(); - fsMock.Verify(fs => fs.Move(newPath, oldPath)); + fsMock.Received().Move(newPath, oldPath); Assert.True(notifedOfAction, "The caller should be notified when an action acts."); Assert.True(notifiedOfReversion, "The caller should be notified when an action is reverted."); @@ -52,15 +50,15 @@ public void HandlesFileOrDirectoryNotExisting() const string fakeDirectory = @"C:\DirectoryThatDoesntExist"; const string fakeFile = @"C:\SomeFakeFile.gif"; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.DirectoryExists(existingDirectory)).Returns(true); - fsMock.Setup(fs => fs.FileExists(existingFile)).Returns(true); - fsMock.Setup(fs => fs.DirectoryExists(fakeDirectory)).Returns(false); - fsMock.Setup(fs => fs.FileExists(fakeFile)).Returns(false); + fsMock.DirectoryExists(existingDirectory).Returns(true); + fsMock.FileExists(existingFile).Returns(true); + fsMock.DirectoryExists(fakeDirectory).Returns(false); + fsMock.FileExists(fakeFile).Returns(false); - Assert.Throws(() => new MoveAction(fakeFile, existingDirectory, fsMock.Object)); + Assert.Throws(() => new MoveAction(fakeFile, existingDirectory, fsMock)); - Assert.Throws(() => new MoveAction(existingFile, fakeDirectory, fsMock.Object)); + Assert.Throws(() => new MoveAction(existingFile, fakeDirectory, fsMock)); } } \ No newline at end of file diff --git a/tests/ImageSort.UnitTests/Actions/RenameActionTests.cs b/tests/ImageSort.UnitTests/Actions/RenameActionTests.cs index 47ff2a3b..c1e73d8e 100644 --- a/tests/ImageSort.UnitTests/Actions/RenameActionTests.cs +++ b/tests/ImageSort.UnitTests/Actions/RenameActionTests.cs @@ -1,7 +1,7 @@ using System.IO; using ImageSort.Actions; using ImageSort.FileSystem; -using Moq; +using NSubstitute; using Xunit; namespace ImageSort.UnitTests.Actions; @@ -18,23 +18,21 @@ public void CanRenameFilesAndUndo() var canAct = false; var canRevert = false; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.FileExists(oldPath)).Returns(true); - fsMock.Setup(fs => fs.FileExists(newPath)).Returns(false); - fsMock.Setup(fs => fs.Move(oldPath, newPath)).Verifiable(); - fsMock.Setup(fs => fs.Move(newPath, oldPath)).Verifiable(); + fsMock.FileExists(oldPath).Returns(true); + fsMock.FileExists(newPath).Returns(false); - var renameAction = new RenameAction(oldPath, newFileName, fsMock.Object, + var renameAction = new RenameAction(oldPath, newFileName, fsMock, (o, n) => canAct = true, (n, o) => canRevert = true); renameAction.Act(); - fsMock.Verify(fs => fs.Move(oldPath, newPath)); + fsMock.Received().Move(oldPath, newPath); renameAction.Revert(); - fsMock.Verify(fs => fs.Move(newPath, oldPath)); + fsMock.Received().Move(newPath, oldPath); Assert.True(canAct); Assert.True(canRevert); @@ -50,18 +48,18 @@ public void ThrowsWhenFileDoesNotExistOrNewPathIsAlreadyUsed() const string alreadyExistingName = @"already-exists"; const string alreadyExistingPath = @"C:\already-exists.png"; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.FileExists(oldPath)).Returns(true); - fsMock.Setup(fs => fs.FileExists(newPath)).Returns(false); - fsMock.Setup(fs => fs.FileExists(invalidOldPath)).Returns(false).Verifiable(); - fsMock.Setup(fs => fs.FileExists(alreadyExistingPath)).Returns(true).Verifiable(); + fsMock.FileExists(oldPath).Returns(true); + fsMock.FileExists(newPath).Returns(false); + fsMock.FileExists(invalidOldPath).Returns(false); + fsMock.FileExists(alreadyExistingPath).Returns(true); - Assert.Throws(() => new RenameAction(invalidOldPath, newFileName, fsMock.Object)); + Assert.Throws(() => new RenameAction(invalidOldPath, newFileName, fsMock)); - Assert.Throws(() => new RenameAction(oldPath, alreadyExistingName, fsMock.Object)); + Assert.Throws(() => new RenameAction(oldPath, alreadyExistingName, fsMock)); - fsMock.Verify(fs => fs.FileExists(invalidOldPath)); - fsMock.Verify(fs => fs.FileExists(alreadyExistingPath)); + fsMock.Received().FileExists(invalidOldPath); + fsMock.Received().FileExists(alreadyExistingPath); } } \ No newline at end of file diff --git a/tests/ImageSort.UnitTests/ViewModels/ActionsViewModelTest.cs b/tests/ImageSort.UnitTests/ViewModels/ActionsViewModelTest.cs index 48f96f7a..faf66c03 100644 --- a/tests/ImageSort.UnitTests/ViewModels/ActionsViewModelTest.cs +++ b/tests/ImageSort.UnitTests/ViewModels/ActionsViewModelTest.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using ImageSort.Actions; using ImageSort.ViewModels; -using Moq; +using NSubstitute; using Xunit; namespace ImageSort.UnitTests.ViewModels; @@ -20,13 +20,11 @@ public async Task WorksCorrectly() var actionsVM = new ActionsViewModel(); - var actionMock = new Mock(); + var actionMock = Substitute.For(); - actionMock.Setup(a => a.Act()).Verifiable(); - actionMock.Setup(a => a.Revert()).Verifiable(); - actionMock.SetupGet(a => a.DisplayName).Returns(actionDisplayName); + actionMock.DisplayName.Returns(actionDisplayName); - await actionsVM.Execute.Execute(actionMock.Object); + await actionsVM.Execute.Execute(actionMock); Assert.Equal(actionDisplayName, actionsVM.LastDone); @@ -44,8 +42,8 @@ public async Task WorksCorrectly() Assert.Equal(actionDisplayName, actionsVM.LastDone); Assert.NotEqual(actionDisplayName, actionsVM.LastUndone); - actionMock.Verify(a => a.Act(), Times.Exactly(2)); - actionMock.Verify(a => a.Revert(), Times.Once); + actionMock.Received(2).Act(); + actionMock.Received(1).Revert(); // make sure clearing works await actionsVM.Clear.Execute(); @@ -60,28 +58,28 @@ public async Task WorksCorrectly() public async Task NotifiesUserOfErrors() { // configure an action that fails when executed - var failingActMock = new Mock(); + var failingActMock = Substitute.For(); - failingActMock.Setup(a => a.Act()).Throws(new Exception("Act doesn't work")); + failingActMock.When(a => a.Act()).Do(x => { throw new Exception("Act doesn't work"); }); // configure an action that fails on reversion (on undo) - var failingRevertMock = new Mock(); + var failingRevertMock = Substitute.For(); - failingRevertMock.Setup(a => a.Revert()).Throws(new Exception("Revert doesn't work")); - failingRevertMock.Setup(a => a.Act()); + failingRevertMock.When(a => a.Revert()).Do(x => { throw new Exception("Revert doesn't work"); }); // configure an action that fails on the second time being executed (on redo) - var failingActOnUndoMock = new Mock(); + var failingActOnUndoMock = Substitute.For(); var timesCalled = 0; - failingActOnUndoMock.Setup(a => a.Revert()); - failingActOnUndoMock.Setup(a => a.Act()).Callback(() => + failingActOnUndoMock.When(a => a.Act()).Do(x => + { timesCalled = timesCalled switch { 0 => 1, _ => throw new Exception("Act doesn't work") - }); + }; + }); var actionsVM = new ActionsViewModel(); @@ -95,14 +93,14 @@ public async Task NotifiesUserOfErrors() }); // fails on execute - await actionsVM.Execute.Execute(failingActMock.Object); + await actionsVM.Execute.Execute(failingActMock); // fails on undo - await actionsVM.Execute.Execute(failingRevertMock.Object); + await actionsVM.Execute.Execute(failingRevertMock); await actionsVM.Undo.Execute(); // fails on redo - await actionsVM.Execute.Execute(failingActOnUndoMock.Object); + await actionsVM.Execute.Execute(failingActOnUndoMock); await actionsVM.Undo.Execute(); await actionsVM.Redo.Execute(); diff --git a/tests/ImageSort.UnitTests/ViewModels/FolderTreeItemViewModelTests.cs b/tests/ImageSort.UnitTests/ViewModels/FolderTreeItemViewModelTests.cs index d981c69e..4e700d15 100644 --- a/tests/ImageSort.UnitTests/ViewModels/FolderTreeItemViewModelTests.cs +++ b/tests/ImageSort.UnitTests/ViewModels/FolderTreeItemViewModelTests.cs @@ -4,11 +4,9 @@ using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; -using Castle.Core.Internal; using ImageSort.FileSystem; using ImageSort.ViewModels; -using Microsoft.Reactive.Testing; -using Moq; +using NSubstitute; using ReactiveUI; using Xunit; @@ -23,24 +21,24 @@ public void ObtainsChildrenCorrectly() var resultingPaths = new[] - { - @"\folder 1", - @"\folder 2", - @"\folder 3" - } - .Select(sub => path + sub); // make the (mock) subfolders absolute paths. + { + @"\folder 1", + @"\folder 2", + @"\folder 3" + } + .Select(sub => path + sub); // make the (mock) subfolders absolute paths. - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetSubFolders(path)).Returns(resultingPaths).Verifiable(); + fsMock.GetSubFolders(path).Returns(resultingPaths); - var folderTreeItem = new FolderTreeItemViewModel(fsMock.Object, backgroundScheduler: RxApp.MainThreadScheduler) + var folderTreeItem = new FolderTreeItemViewModel(fsMock, backgroundScheduler: RxApp.MainThreadScheduler) { Path = path, IsVisible = true }; - fsMock.Verify(fs => fs.GetSubFolders(path)); + fsMock.Received().GetSubFolders(path); while (folderTreeItem.Children.Count == 0) {} @@ -53,11 +51,11 @@ public void HandlesUnauthorizedAccessExceptionGracefully() { const string pathToUnauthorisedFolder = @"C:\UnauthorizedFolder"; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetSubFolders(pathToUnauthorisedFolder)).Throws(new UnauthorizedAccessException()); + fsMock.GetSubFolders(pathToUnauthorisedFolder).Returns(x => throw new UnauthorizedAccessException()); - var folderTreeItem = new FolderTreeItemViewModel(fsMock.Object) + var folderTreeItem = new FolderTreeItemViewModel(fsMock) { Path = pathToUnauthorisedFolder }; @@ -76,30 +74,22 @@ public async Task CanCreateFolders() result.AddRange(subfolders); result.Add(addedFolder); - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetSubFolders(currentFolder)).Returns(subfolders); - fsMock.Setup(fs => fs.CreateFolder(addedFolder)).Verifiable(); + fsMock.GetSubFolders(currentFolder).Returns(subfolders); + fsMock.CreateFolder(addedFolder); - var testScheduler = new TestScheduler(); - - testScheduler.Start(); - - var folderTreeItem = new FolderTreeItemViewModel(fsMock.Object, backgroundScheduler: RxApp.MainThreadScheduler) + var folderTreeItem = new FolderTreeItemViewModel(fsMock, backgroundScheduler: RxApp.MainThreadScheduler) { Path = currentFolder, IsVisible = true }; - testScheduler.AdvanceBy(1); - await folderTreeItem.CreateFolder.Execute(addedFolder); // verify that no second folder is created when a folder already exists await folderTreeItem.CreateFolder.Execute(addedFolder); - testScheduler.AdvanceBy(1); - - fsMock.Verify(fs => fs.CreateFolder(addedFolder)); + fsMock.Received().CreateFolder(addedFolder); Assert.Equal(result.OrderBy(p => p), folderTreeItem.Children.Select(f => f.Path).OrderBy(p => p)); } @@ -111,24 +101,24 @@ public void DoNotLoadSubfoldersWhenNotVisible() var resultingPaths = new[] - { - @"\folder 1", - @"\folder 2", - @"\folder 3" - } - .Select(sub => path + sub); // make the (mock) subfolders absolute paths. + { + @"\folder 1", + @"\folder 2", + @"\folder 3" + } + .Select(sub => path + sub); // make the (mock) subfolders absolute paths. - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetSubFolders(path)).Returns(resultingPaths).Verifiable(); + fsMock.GetSubFolders(path).Returns(resultingPaths); - var folderTreeItem = new FolderTreeItemViewModel(fsMock.Object, backgroundScheduler: RxApp.MainThreadScheduler) + var folderTreeItem = new FolderTreeItemViewModel(fsMock, backgroundScheduler: RxApp.MainThreadScheduler) { Path = path, IsVisible = false }; - fsMock.Verify(fs => fs.GetSubFolders(path), Times.Never()); + fsMock.DidNotReceive().GetSubFolders(path); Assert.Empty(folderTreeItem.Children.Select(vm => vm.Path).ToArray()); } diff --git a/tests/ImageSort.UnitTests/ViewModels/FoldersViewModelTests.cs b/tests/ImageSort.UnitTests/ViewModels/FoldersViewModelTests.cs index 2d9eafb5..f0703531 100644 --- a/tests/ImageSort.UnitTests/ViewModels/FoldersViewModelTests.cs +++ b/tests/ImageSort.UnitTests/ViewModels/FoldersViewModelTests.cs @@ -4,11 +4,10 @@ using System.Threading.Tasks; using ImageSort.FileSystem; using ImageSort.ViewModels; -using Moq; +using NSubstitute; using Xunit; namespace ImageSort.UnitTests.ViewModels; - public class FoldersViewModelTests { private const string MockPath = @"C:\SomePath\"; @@ -16,10 +15,10 @@ public class FoldersViewModelTests public FoldersViewModelTests() { - var fsMock = new Mock(); - fsMock.Setup(fs => fs.GetSubFolders(It.IsAny())).Returns(Array.Empty()); + var fsMock = Substitute.For(); + fsMock.GetSubFolders(Arg.Any()).Returns(Array.Empty()); - fileSystemMock = fsMock.Object; + fileSystemMock = fsMock; } private FolderTreeItemViewModel CreateMock(string path) @@ -46,11 +45,11 @@ public async Task CanPinFolders() { const string mockPathToPin = @"C:\SomeOtherPath\"; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetSubFolders(It.IsAny())).Returns(Enumerable.Empty); + fsMock.GetSubFolders(Arg.Any()).Returns(Enumerable.Empty()); - var foldersVM = new FoldersViewModel(fsMock.Object) + var foldersVM = new FoldersViewModel(fsMock) { CurrentFolder = CreateMock(MockPath) }; @@ -104,11 +103,11 @@ public async Task CanUnpinSelected() { const string mockPathToPin = @"C:\SomeOtherPath\"; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetSubFolders(It.IsAny())).Returns(Enumerable.Empty); + fsMock.GetSubFolders(Arg.Any()).Returns(Enumerable.Empty()); - var foldersVM = new FoldersViewModel(fsMock.Object) + var foldersVM = new FoldersViewModel(fsMock) { CurrentFolder = CreateMock(MockPath) }; @@ -141,38 +140,6 @@ public async Task OnlyUnpinsIfPinned() Assert.False(await foldersVM.UnpinSelected.CanExecute.FirstAsync()); } - [Fact(DisplayName = "Concatenates the current folder and the pinned folders correctly.")] - public async Task ConcatenatesFoldersCorrectly() - { - var currentFolder = CreateMock(MockPath); - - var fsMock = new Mock(); - - fsMock.Setup(fs => fs.GetSubFolders(It.IsAny())).Returns(Enumerable.Empty); - - var foldersVM = new FoldersViewModel(fsMock.Object) - { - CurrentFolder = currentFolder - }; - - var mockFolders = new[] - { - @"C:\SomeOtherPath1\", - @"C:\SomeOtherPath2\", - @"C:\SomeOtherPath3\" - }; - - foreach (var mockFolder in mockFolders) - { - foldersVM.SelectFolder.RegisterHandler(interaction => { interaction.SetOutput(mockFolder); }); - - await foldersVM.Pin.Execute(); - } - - Assert.Equal(new[] {currentFolder.Path}.Concat(mockFolders), - foldersVM.AllFoldersTracked.Select(f => f.Path)); - } - [Fact(DisplayName = "Marks the current folder as such")] public void MarksCurrentFolder() { @@ -194,15 +161,13 @@ public async Task MovesSelectedFolderUp() CurrentFolder = CreateMock(MockPath) }; - var pinnedFolders = new[] {@"C:\folder 1", @"C:\folder 2", @"C:\folder 3"}; + var pinnedFolders = new[] { @"C:\folder 1", @"C:\folder 2", @"C:\folder 3" }; foreach (var pinnedFolder in pinnedFolders) { - var handler = foldersVM.SelectFolder.RegisterHandler(ic => ic.SetOutput(pinnedFolder)); + foldersVM.SelectFolder.RegisterHandler(ic => ic.SetOutput(pinnedFolder)); await foldersVM.Pin.Execute(); - - handler.Dispose(); } foldersVM.Selected = foldersVM.PinnedFolders.ElementAt(0); @@ -227,15 +192,13 @@ public async Task MovesSelectedFolderDown() CurrentFolder = CreateMock(MockPath) }; - var pinnedFolders = new[] {@"C:\folder 1", @"C:\folder 2", @"C:\folder 3"}; + var pinnedFolders = new[] { @"C:\folder 1", @"C:\folder 2", @"C:\folder 3" }; foreach (var pinnedFolder in pinnedFolders) { - var handler = foldersVM.SelectFolder.RegisterHandler(ic => ic.SetOutput(pinnedFolder)); + foldersVM.SelectFolder.RegisterHandler(ic => ic.SetOutput(pinnedFolder)); await foldersVM.Pin.Execute(); - - handler.Dispose(); } foldersVM.Selected = foldersVM.PinnedFolders.ElementAt(2); diff --git a/tests/ImageSort.UnitTests/ViewModels/ImagesViewModelTests.cs b/tests/ImageSort.UnitTests/ViewModels/ImagesViewModelTests.cs index e40f7e27..7b59a348 100644 --- a/tests/ImageSort.UnitTests/ViewModels/ImagesViewModelTests.cs +++ b/tests/ImageSort.UnitTests/ViewModels/ImagesViewModelTests.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using ImageSort.FileSystem; using ImageSort.ViewModels; -using Moq; +using NSubstitute; using Xunit; namespace ImageSort.UnitTests.ViewModels; @@ -23,16 +23,16 @@ public void GetTheFilesCorrectly() f.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)); - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetFiles(basePath)).Returns(allFiles); + fsMock.GetFiles(basePath).Returns(allFiles); - var imagesVM = new ImagesViewModel(fsMock.Object) + var imagesVM = new ImagesViewModel(fsMock) { CurrentFolder = basePath }; - fsMock.Verify(fs => fs.GetFiles(basePath)); + fsMock.Received().GetFiles(basePath); Assert.Equal(expectedFiles, imagesVM.Images); } @@ -44,11 +44,11 @@ public void SelectedImageWorksCorrectly() var allFiles = new[] {"image.png", "some.gif"} .Select(f => basePath + f); - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetFiles(basePath)).Returns(allFiles); + fsMock.GetFiles(basePath).Returns(allFiles); - var imagesVM = new ImagesViewModel(fsMock.Object) + var imagesVM = new ImagesViewModel(fsMock) { CurrentFolder = basePath }; @@ -69,11 +69,11 @@ public void CanRemoveAndAddImagesExternally() var allFiles = new[] {"image.png", "some.gif"} .Select(f => basePath + f); - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetFiles(basePath)).Returns(allFiles); + fsMock.GetFiles(basePath).Returns(allFiles); - var imagesVM = new ImagesViewModel(fsMock.Object) + var imagesVM = new ImagesViewModel(fsMock) { CurrentFolder = basePath }; @@ -98,11 +98,11 @@ public void SearchFilterWorks() var allFiles = new[] {"image.png", "some.gif"} .Select(f => basePath + f); - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetFiles(basePath)).Returns(allFiles); + fsMock.GetFiles(basePath).Returns(allFiles); - var imagesVM = new ImagesViewModel(fsMock.Object) + var imagesVM = new ImagesViewModel(fsMock) { CurrentFolder = basePath }; @@ -128,15 +128,15 @@ public async Task CanRenameImages() var notifiesUserOfError = false; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetFiles(basePath)).Returns(allFiles); + fsMock.GetFiles(basePath).Returns(allFiles); - fsMock.Setup(fs => fs.Move(oldFilePath, newFilePath)).Verifiable(); + fsMock.Move(oldFilePath, newFilePath); - fsMock.Setup(fs => fs.FileExists(oldFilePath)).Returns(true); + fsMock.FileExists(oldFilePath).Returns(true); - var imagesVM = new ImagesViewModel(fsMock.Object) + var imagesVM = new ImagesViewModel(fsMock) { CurrentFolder = basePath }; @@ -156,7 +156,7 @@ public async Task CanRenameImages() await imagesVM.RenameImage.Execute(); - fsMock.Verify(fs => fs.Move(oldFilePath, newFilePath)); + fsMock.Received().Move(oldFilePath, newFilePath); await Task.Delay(1); diff --git a/tests/ImageSort.UnitTests/ViewModels/MainViewModelTests.cs b/tests/ImageSort.UnitTests/ViewModels/MainViewModelTests.cs index 6aa03802..cb3e1dcc 100644 --- a/tests/ImageSort.UnitTests/ViewModels/MainViewModelTests.cs +++ b/tests/ImageSort.UnitTests/ViewModels/MainViewModelTests.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using ImageSort.FileSystem; using ImageSort.ViewModels; -using Moq; +using NSubstitute; using ReactiveUI; using Xunit; @@ -17,24 +17,24 @@ public class MainViewModelTests public MainViewModelTests() { - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.GetSubFolders(@"C:\")).Returns(new[] {@"C:\folder"}); - fsMock.Setup(fs => fs.GetSubFolders(It.IsAny())).Returns(Enumerable.Empty); + fsMock.GetSubFolders(@"C:\").Returns(new[] { @"C:\folder" }); + fsMock.GetSubFolders(Arg.Any()).Returns(Enumerable.Empty()); - fsMock.Setup(fs => fs.GetFiles(It.IsAny())) - .Returns(new[] {@"c:\img.png"}); // just so that no exception is thrown + fsMock.GetFiles(Arg.Any()) + .Returns(new[] { @"c:\img.png" }); // just so that no exception is thrown - mainVM = new MainViewModel(fsMock.Object) + mainVM = new MainViewModel(fsMock) { - Images = new ImagesViewModel(fsMock.Object) + Images = new ImagesViewModel(fsMock) { CurrentFolder = @"C:\" }, - Folders = new FoldersViewModel(fsMock.Object) + Folders = new FoldersViewModel(fsMock) { - CurrentFolder = new FolderTreeItemViewModel(fsMock.Object) {Path = @"C:\"}, - Selected = new FolderTreeItemViewModel(fsMock.Object) {Path = @"C:\folder"} + CurrentFolder = new FolderTreeItemViewModel(fsMock) { Path = @"C:\" }, + Selected = new FolderTreeItemViewModel(fsMock) { Path = @"C:\folder" } } }; } @@ -98,25 +98,23 @@ public async Task CanMoveImages() const string newDirectory = @"C:\Some other folder"; const string moveDestination = newDirectory + @"\some image.png"; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.DirectoryExists(currentDirectory)).Returns(true); - fsMock.Setup(fs => fs.FileExists(image)).Returns(true); - fsMock.Setup(fs => fs.DirectoryExists(newDirectory)).Returns(true); + fsMock.DirectoryExists(currentDirectory).Returns(true); + fsMock.FileExists(image).Returns(true); + fsMock.DirectoryExists(newDirectory).Returns(true); - fsMock.Setup(fs => fs.GetFiles(currentDirectory)).Returns(new[] {image}); + fsMock.GetFiles(currentDirectory).Returns(new[] {image}); - fsMock.Setup(fs => fs.Move(image, moveDestination)).Verifiable(); - - var otherMainVM = new MainViewModel(fsMock.Object, backgroundScheduler: RxApp.MainThreadScheduler) + var otherMainVM = new MainViewModel(fsMock, backgroundScheduler: RxApp.MainThreadScheduler) { Actions = new ActionsViewModel(), - Folders = new FoldersViewModel(fsMock.Object, RxApp.MainThreadScheduler) + Folders = new FoldersViewModel(fsMock, RxApp.MainThreadScheduler) { - CurrentFolder = new FolderTreeItemViewModel(fsMock.Object, backgroundScheduler: RxApp.MainThreadScheduler) + CurrentFolder = new FolderTreeItemViewModel(fsMock, backgroundScheduler: RxApp.MainThreadScheduler) {Path = currentDirectory} }, - Images = new ImagesViewModel(fsMock.Object) + Images = new ImagesViewModel(fsMock) }; otherMainVM.Images.SelectedIndex = 0; @@ -141,40 +139,37 @@ public async Task CanMoveImages() Assert.Empty(otherMainVM.Images.Images); - fsMock.Verify(fs => fs.Move(image, moveDestination)); + fsMock.Received().Move(image, moveDestination); } - [Fact(DisplayName = - "Can delete images and registers that action, removing the image from the images viewmodel in the process.")] + [Fact(DisplayName = "Can delete images and registers that action, removing the image from the images viewmodel in the process.")] public async Task CanDeleteImages() { const string currentDirectory = @"C:\Some Folder With Pictures"; const string image = currentDirectory + @"\some image.png"; - var fsMock = new Mock(); + var fsMock = Substitute.For(); - fsMock.Setup(fs => fs.DirectoryExists(currentDirectory)).Returns(true); - fsMock.Setup(fs => fs.FileExists(image)).Returns(true); + fsMock.DirectoryExists(currentDirectory).Returns(true); + fsMock.FileExists(image).Returns(true); - fsMock.Setup(fs => fs.GetFiles(currentDirectory)).Returns(new[] {image}); + fsMock.GetFiles(currentDirectory).Returns(new[] { image }); - var restorerMock = new Mock(); + var restorerMock = Substitute.For(); - restorerMock.Setup(r => r.Dispose()).Verifiable(); + var rbMock = Substitute.For(); - var rbMock = new Mock(); + rbMock.Send(image, false).Returns(restorerMock); - rbMock.Setup(rb => rb.Send(image, false)).Returns(restorerMock.Object); - - var otherMainVM = new MainViewModel(fsMock.Object, rbMock.Object, RxApp.MainThreadScheduler) + var otherMainVM = new MainViewModel(fsMock, rbMock, RxApp.MainThreadScheduler) { Actions = new ActionsViewModel(), - Folders = new FoldersViewModel(fsMock.Object, RxApp.MainThreadScheduler) + Folders = new FoldersViewModel(fsMock, RxApp.MainThreadScheduler) { - CurrentFolder = new FolderTreeItemViewModel(fsMock.Object, backgroundScheduler: RxApp.MainThreadScheduler) - {Path = currentDirectory} + CurrentFolder = new FolderTreeItemViewModel(fsMock, backgroundScheduler: RxApp.MainThreadScheduler) + { Path = currentDirectory } }, - Images = new ImagesViewModel(fsMock.Object) + Images = new ImagesViewModel(fsMock) }; otherMainVM.Images.SelectedIndex = 0; @@ -187,6 +182,6 @@ public async Task CanDeleteImages() await otherMainVM.Actions.Undo.Execute(); - restorerMock.Verify(r => r.Dispose()); + restorerMock.Received().Dispose(); } } \ No newline at end of file diff --git a/tests/ImageSort.UnitTests/ViewModels/MetadataViewModelTests.cs b/tests/ImageSort.UnitTests/ViewModels/MetadataViewModelTests.cs index 8cc3dd1a..8d6f4909 100644 --- a/tests/ImageSort.UnitTests/ViewModels/MetadataViewModelTests.cs +++ b/tests/ImageSort.UnitTests/ViewModels/MetadataViewModelTests.cs @@ -1,6 +1,6 @@ using ImageSort.FileSystem; using ImageSort.ViewModels.Metadata; -using Moq; +using NSubstitute; using System; using System.Collections.Generic; using System.Linq; @@ -14,12 +14,12 @@ public class MetadataViewModelTests { private readonly MetadataViewModel metadataViewModel; - private readonly Mock fileSystem = new(); - private readonly Mock metadataExtractor = new(); + private readonly IFileSystem fileSystem = Substitute.For(); + private readonly IMetadataExtractor metadataExtractor = Substitute.For(); public MetadataViewModelTests() { - metadataViewModel = new(metadataExtractor.Object, fileSystem.Object, new MetadataSectionViewModelFactory(new MetadataFieldViewModelFactory())); + metadataViewModel = new(metadataExtractor, fileSystem, new MetadataSectionViewModelFactory(new MetadataFieldViewModelFactory())); } [Fact(DisplayName = "MetadataViewModel should extract metadata from image")] @@ -33,13 +33,10 @@ public void ExtractsMetadataWhenPathIsSet() } } }; - - fileSystem.Setup(x => x.FileExists(thisFileExists)).Returns(true) - .Verifiable("Should check whether or not the file exists"); - metadataExtractor.Setup(x => x.Extract(thisFileExists)) - .Returns(extractableMetadata) - .Verifiable("Should extract metadata from image"); + fileSystem.FileExists(thisFileExists).Returns(true); + + metadataExtractor.Extract(thisFileExists).Returns(extractableMetadata); // this should cause the extraction of metadata metadataViewModel.ImagePath = thisFileExists; @@ -47,8 +44,8 @@ public void ExtractsMetadataWhenPathIsSet() Assert.Equal(MetadataResultType.Success, metadataViewModel.Metadata.Type); Assert.Equal(extractableMetadata, metadataViewModel.Metadata.Metadata); - fileSystem.Verify(x => x.FileExists(thisFileExists)); - metadataExtractor.Verify(x => x.Extract(thisFileExists)); + fileSystem.Received().FileExists(thisFileExists); + metadataExtractor.Received().Extract(thisFileExists); } [Fact(DisplayName = "MetadataViewModel should not extract metadata from image when file does not exist")] @@ -56,21 +53,16 @@ public void DoesNotExtractMetadataWhenPathIsSetAndFileDoesNotExist() { // setup mocks and view model string thisFileDoesNotExist = "C:\\test2.jpg"; - - fileSystem.Setup(x => x.FileExists(thisFileDoesNotExist)).Returns(false) - .Verifiable("Should check whether or not the file exists"); - - metadataExtractor.Setup(x => x.Extract(thisFileDoesNotExist)) - .Throws(new Exception("Should not extract metadata from image when file does not exist")) - .Verifiable("Should not extract metadata from image when the file does not exist"); + + fileSystem.FileExists(thisFileDoesNotExist).Returns(false); metadataViewModel.ImagePath = thisFileDoesNotExist; Assert.Equal(MetadataResultType.FileDoesNotExist, metadataViewModel.Metadata.Type); Assert.Null(metadataViewModel.Metadata.Metadata); - - fileSystem.Verify(x => x.FileExists(thisFileDoesNotExist)); - metadataExtractor.Verify(x => x.Extract(thisFileDoesNotExist), Times.Never); + + fileSystem.Received().FileExists(thisFileDoesNotExist); + metadataExtractor.DidNotReceive().Extract(thisFileDoesNotExist); } [Fact(DisplayName = "Correctly reports unhandled exceptions that occur when trying to extract metadata")] @@ -79,22 +71,19 @@ public void CorrectlyReportsIssuesWithTheExtractionOfMetadata() // setup mocks and view model string thisFileHasInvalidMetadata = "C:\\test3.jpg"; Exception invalidMetadata = new("Invalid metadata could not be loaded"); - - fileSystem.Setup(x => x.FileExists(thisFileHasInvalidMetadata)).Returns(true) - .Verifiable("Should check whether or not the file exists"); - - metadataExtractor.Setup(x => x.Extract(thisFileHasInvalidMetadata)) - .Throws(invalidMetadata) - .Verifiable("Should extract metadata from image when the file does exist"); - + + fileSystem.FileExists(thisFileHasInvalidMetadata).Returns(true); + + metadataExtractor.Extract(thisFileHasInvalidMetadata).Returns(x => throw invalidMetadata); + metadataViewModel.ImagePath = thisFileHasInvalidMetadata; Assert.Equal(MetadataResultType.UnexpectedError, metadataViewModel.Metadata.Type); Assert.Null(metadataViewModel.Metadata.Metadata); Assert.Equal(invalidMetadata, metadataViewModel.Metadata.Exception); - fileSystem.Verify(x => x.FileExists(thisFileHasInvalidMetadata)); - metadataExtractor.Verify(x => x.Extract(thisFileHasInvalidMetadata)); + fileSystem.Received().FileExists(thisFileHasInvalidMetadata); + metadataExtractor.Received().Extract(thisFileHasInvalidMetadata); } [Fact(DisplayName = "Correctly creates metadata sections from extracted metadata")] @@ -109,12 +98,9 @@ public void CorrectlyCreatesMetadataSectionsFromExtractedMetadata() } }; - fileSystem.Setup(x => x.FileExists(thisFileHasMetadata)).Returns(true) - .Verifiable("Should check whether or not the file exists"); + fileSystem.FileExists(thisFileHasMetadata).Returns(true); - metadataExtractor.Setup(x => x.Extract(thisFileHasMetadata)) - .Returns(extractableMetadata) - .Verifiable("Should extract metadata from image when the file does exist"); + metadataExtractor.Extract(thisFileHasMetadata).Returns(extractableMetadata); metadataViewModel.ImagePath = thisFileHasMetadata; @@ -122,7 +108,7 @@ public void CorrectlyCreatesMetadataSectionsFromExtractedMetadata() Assert.Equal("test", metadataViewModel.SectionViewModels.First().Title); Assert.Equal(extractableMetadata["test"], metadataViewModel.SectionViewModels.First().Fields); - fileSystem.Verify(x => x.FileExists(thisFileHasMetadata)); - metadataExtractor.Verify(x => x.Extract(thisFileHasMetadata)); + fileSystem.Received().FileExists(thisFileHasMetadata); + metadataExtractor.Received().Extract(thisFileHasMetadata); } -} +} \ No newline at end of file From 7ddcd795662d3ce0451063110aff09c8c99fe559 Mon Sep 17 00:00:00 2001 From: Luca Auer Date: Tue, 12 Dec 2023 18:59:23 +0100 Subject: [PATCH 3/3] Remove moq --- tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj b/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj index 92955f0d..0aa175ce 100644 --- a/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj +++ b/tests/ImageSort.UnitTests/ImageSort.UnitTests.csproj @@ -10,7 +10,6 @@ - all