From 17abf0474c8c4ebbd7385f647840acea8384647a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 9 Nov 2021 17:48:50 +0100 Subject: [PATCH] Added unit tests for ContentVersionService (#11597) * Added unit tests for ContentVersionService * Clean up * Fixed tests after using ConfigureMembers = true * Fixed tests after using ConfigureMembers = true --- .../Implement/ContentVersionService.cs | 11 +- .../AutoFixture/AutoMoqDataAttribute.cs | 17 +- .../ContentVersionCleanupServiceTest.cs | 433 +++++++----------- .../Controllers/MemberControllerUnitTests.cs | 15 + 4 files changed, 203 insertions(+), 273 deletions(-) diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs index 40c12a1fe5..26cffee901 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentVersionService.cs @@ -41,11 +41,14 @@ namespace Umbraco.Cms.Core.Services.Implement /// In v9 this can live in another class as we publish the notifications via IEventAggregator. /// But for v8 must be here for access to the static events. /// - public IReadOnlyCollection PerformContentVersionCleanup(DateTime asAtDate) => - CleanupDocumentVersions(asAtDate); + public IReadOnlyCollection PerformContentVersionCleanup(DateTime asAtDate) + { + // Media - ignored + // Members - ignored + return CleanupDocumentVersions(asAtDate); + } + - // Media - ignored - // Members - ignored /// /// v9 - move to another class /// diff --git a/tests/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs b/tests/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs index 178d480102..f543d847b1 100644 --- a/tests/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs +++ b/tests/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs @@ -21,10 +21,12 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.BackOffice.Controllers; using Umbraco.Cms.Web.BackOffice.Install; using Umbraco.Cms.Web.BackOffice.Routing; +using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Cms.Web.Common.Security; namespace Umbraco.Cms.Tests.UnitTests.AutoFixture { + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)] public class AutoMoqDataAttribute : AutoDataAttribute { @@ -40,6 +42,7 @@ namespace Umbraco.Cms.Tests.UnitTests.AutoFixture internal static class AutoMockCustomizations { + public static IFixture Default => new Fixture().Customize(new UmbracoCustomization()); private class UmbracoCustomization : ICustomization @@ -58,7 +61,7 @@ namespace Umbraco.Cms.Tests.UnitTests.AutoFixture .Customize(new ConstructorCustomization(typeof(BackOfficeUserManager), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(MemberManager), new GreedyConstructorQuery())); - fixture.Customize(new AutoMoqCustomization()); + fixture.Customize(new AutoMoqCustomization(){ConfigureMembers = true}); // When requesting an IUserStore ensure we actually uses a IUserLockoutStore fixture.Customize>(cc => cc.FromFactory(() => Mock.Of>())); @@ -71,6 +74,8 @@ namespace Umbraco.Cms.Tests.UnitTests.AutoFixture u => u.FromFactory( () => new UmbracoVersion())); + fixture.Customize(x => + x.With(settings => settings.ApplicationVirtualPath, string.Empty)); fixture.Customize(u => u.FromFactory( () => new BackOfficeAreaRoutes( Options.Create(new GlobalSettings()), @@ -84,12 +89,18 @@ namespace Umbraco.Cms.Tests.UnitTests.AutoFixture Mock.Of(x => x.ToAbsolute(It.IsAny()) == "/umbraco" && x.ApplicationVirtualPath == string.Empty), Mock.Of(x => x.Level == RuntimeLevel.Run)))); - var connectionStrings = new ConnectionStrings(); - fixture.Customize(x => x.FromFactory(() => connectionStrings)); + var configConnectionString = new ConfigConnectionString("ss", + "Data Source=(localdb)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\\Umbraco.mdf;Integrated Security=True"); + fixture.Customize(x => x.FromFactory(() => configConnectionString)); + var httpContextAccessor = new HttpContextAccessor { HttpContext = new DefaultHttpContext() }; fixture.Customize(x => x.FromFactory(() => httpContextAccessor.HttpContext)); fixture.Customize(x => x.FromFactory(() => httpContextAccessor)); + + fixture.Customize(x => x.With(settings => settings.UmbracoApplicationUrl, "http://localhost:5000")); + + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); } } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentVersionCleanupServiceTest.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentVersionCleanupServiceTest.cs index 00f5e53b47..907c12424a 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentVersionCleanupServiceTest.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentVersionCleanupServiceTest.cs @@ -1,266 +1,167 @@ -// using System; -// using System.Collections.Generic; -// using System.Diagnostics; -// using System.Linq; -// using AutoFixture.NUnit3; -// using CSharpTest.Net.Interfaces; -// using Moq; -// using NUnit.Framework; -// using Umbraco.Cms.Core.Events; -// using Umbraco.Cms.Core.Models; -// using Umbraco.Cms.Core.Scoping; -// using Umbraco.Cms.Core.Services; -// using Umbraco.Cms.Core.Services.Implement; -// using Umbraco.Cms.Tests.UnitTests.AutoFixture; -// using Umbraco.Core.Composing; -// using Umbraco.Core.Events; -// using Umbraco.Core.Models; -// using Umbraco.Core.Persistence.Repositories; -// using Umbraco.Core.Scoping; -// using Umbraco.Core.Services; -// using Umbraco.Core.Services.Implement; -// using Umbraco.Tests.Testing; -// -// namespace Umbraco.Tests.Services -// { -// /// -// /// v9 -> Tests.UnitTests -// /// Sut here is ContentService, but in v9 should be a new class -// /// -// [TestFixture] -// public class ContentVersionCleanupServiceTest -// { -// -// -// /// -// /// For v9 this just needs a rewrite, no static events, no service location etc -// /// -// [Test, AutoMoqData] -// public void PerformContentVersionCleanup_Always_RespectsDeleteRevisionsCancellation( -// [Frozen] Mock scope, -// Mock documentVersionRepository, -// List someHistoricVersions, -// DateTime aDateTime, -// ContentService sut) -// { -// factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) -// .Returns(documentVersionRepository.Object); -// -// factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) -// .Returns(new EchoingCleanupPolicyStub()); -// -// documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) -// .Returns(someHistoricVersions); -// -// scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); -// -// // Wire up service locator -// Current.Factory = factory.Object; -// -// void OnDeletingVersions(IContentService sender, DeleteRevisionsEventArgs args) => args.Cancel = true; -// -// ContentService.DeletingVersions += OnDeletingVersions; -// -// // # Act -// var report = sut.PerformContentVersionCleanup(aDateTime); -// -// ContentService.DeletingVersions -= OnDeletingVersions; -// -// Assert.AreEqual(0, report.Count); -// } -// -// /// -// /// For v9 this just needs a rewrite, no static events, no service location etc -// /// -// [Test, AutoMoqData] -// public void PerformContentVersionCleanup_Always_FiresDeletedVersionsForEachDeletedVersion( -// [Frozen] Mock factory, -// [Frozen] Mock scope, -// Mock documentVersionRepository, -// List someHistoricVersions, -// DateTime aDateTime, -// ContentService sut) -// { -// factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) -// .Returns(documentVersionRepository.Object); -// -// factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) -// .Returns(new EchoingCleanupPolicyStub()); -// -// documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) -// .Returns(someHistoricVersions); -// -// scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); -// -// // Wire up service locator -// Current.Factory = factory.Object; -// -// // v9 can Mock + Verify -// var deletedAccordingToEvents = 0; -// void OnDeletedVersions(IContentService sender, DeleteRevisionsEventArgs args) => deletedAccordingToEvents++; -// -// ContentService.DeletedVersions += OnDeletedVersions; -// -// // # Act -// sut.PerformContentVersionCleanup(aDateTime); -// -// ContentService.DeletedVersions -= OnDeletedVersions; -// -// Assert.Multiple(() => -// { -// Assert.Greater(deletedAccordingToEvents, 0); -// Assert.AreEqual(someHistoricVersions.Count, deletedAccordingToEvents); -// }); -// } -// -// /// -// /// For v9 this just needs a rewrite, no static events, no service location etc -// /// -// [Test, AutoMoqData] -// public void PerformContentVersionCleanup_Always_ReturnsReportOfDeletedItems( -// [Frozen] Mock factory, -// [Frozen] Mock scope, -// Mock documentVersionRepository, -// List someHistoricVersions, -// DateTime aDateTime, -// ContentService sut) -// { -// factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) -// .Returns(documentVersionRepository.Object); -// -// factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) -// .Returns(new EchoingCleanupPolicyStub()); -// -// documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) -// .Returns(someHistoricVersions); -// -// scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); -// -// // Wire up service locator -// Current.Factory = factory.Object; -// -// // # Act -// var report = sut.PerformContentVersionCleanup(aDateTime); -// -// Assert.Multiple(() => -// { -// Assert.Greater(report.Count, 0); -// Assert.AreEqual(someHistoricVersions.Count, report.Count); -// }); -// } -// -// /// -// /// For v9 this just needs a rewrite, no static events, no service location etc -// /// -// [Test, AutoMoqData] -// public void PerformContentVersionCleanup_Always_AdheresToCleanupPolicy( -// [Frozen] Mock factory, -// [Frozen] Mock scope, -// Mock documentVersionRepository, -// Mock cleanupPolicy, -// List someHistoricVersions, -// DateTime aDateTime, -// ContentService sut) -// { -// factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) -// .Returns(documentVersionRepository.Object); -// -// factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) -// .Returns(cleanupPolicy.Object); -// -// documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) -// .Returns(someHistoricVersions); -// -// scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); -// -// cleanupPolicy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) -// .Returns>((_, items) => items.Take(1)); -// -// // Wire up service locator -// Current.Factory = factory.Object; -// -// // # Act -// var report = sut.PerformContentVersionCleanup(aDateTime); -// -// Debug.Assert(someHistoricVersions.Count > 1); -// -// Assert.Multiple(() => -// { -// cleanupPolicy.Verify(x => x.Apply(aDateTime, someHistoricVersions), Times.Once); -// Assert.AreEqual(someHistoricVersions.First(), report.Single()); -// }); -// } -// -// /// -// /// For v9 this just needs a rewrite, no static events, no service location etc -// /// -// [Test, AutoMoqData] -// public void PerformContentVersionCleanup_HasVersionsToDelete_CallsDeleteOnRepositoryWithFilteredSet( -// [Frozen] Mock factory, -// [Frozen] Mock scope, -// Mock documentVersionRepository, -// Mock cleanupPolicy, -// List someHistoricVersions, -// DateTime aDateTime, -// ContentService sut) -// { -// factory.Setup(x => x.GetInstance(typeof(IDocumentVersionRepository))) -// .Returns(documentVersionRepository.Object); -// -// factory.Setup(x => x.GetInstance(typeof(IContentVersionCleanupPolicy))) -// .Returns(cleanupPolicy.Object); -// -// documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) -// .Returns(someHistoricVersions); -// -// scope.Setup(x => x.Events).Returns(new PassThroughEventDispatcher()); -// -// var filteredSet = someHistoricVersions.Take(1); -// -// cleanupPolicy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) -// .Returns>((_, items) => filteredSet); -// -// // Wire up service locator -// Current.Factory = factory.Object; -// -// // # Act -// var report = sut.PerformContentVersionCleanup(aDateTime); -// -// Debug.Assert(someHistoricVersions.Any()); -// -// var expectedId = filteredSet.First().VersionId; -// -// documentVersionRepository.Verify(x => x.DeleteVersions(It.Is>(y => y.Single() == expectedId)), Times.Once); -// } -// -// class EchoingCleanupPolicyStub : IContentVersionCleanupPolicy -// { -// /// -// /// What goes in, must come out -// /// -// public EchoingCleanupPolicyStub() { } -// -// /* Note: Could just wire up a mock but its quite wordy. -// * -// * cleanupPolicy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) -// * .Returns>((date, items) => items); -// */ -// public IEnumerable Apply( -// DateTime asAtDate, -// IEnumerable items -// ) => items; -// } -// -// /// -// /// NPoco < 5 requires a parameter-less constructor but plays nice with get-only properties. -// /// Moq won't play nice with get-only properties, but doesn't require a parameter-less constructor. -// /// -// /// Inheritance solves this so that we get values for test data without a specimen builder -// /// -// public class TestHistoricContentVersionMeta : HistoricContentVersionMeta -// { -// public TestHistoricContentVersionMeta(int contentId, int contentTypeId, int versionId, DateTime versionDate) -// : base(contentId, contentTypeId, versionId, versionDate) { } -// } -// } -// } +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using AutoFixture.NUnit3; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Tests.UnitTests.AutoFixture; + +namespace Umbraco.Tests.Services +{ + [TestFixture] + internal class ContentVersionCleanupServiceTest + { + [Test] + [AutoMoqData] + public void PerformContentVersionCleanup_Always_RespectsDeleteRevisionsCancellation( + [Frozen] Mock eventAggregator, + [Frozen] Mock policy, + [Frozen] Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentVersionService sut) + { + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + eventAggregator.Setup(x => x.PublishCancelable(It.IsAny())) + .Returns(true); + + policy.Setup(x => x.Apply(aDateTime, someHistoricVersions)) + .Returns(someHistoricVersions); + + // # Act + IReadOnlyCollection report = sut.PerformContentVersionCleanup(aDateTime); + + Assert.Multiple(() => + { + eventAggregator.Verify(x => x.PublishCancelable(It.IsAny()), Times.Exactly(someHistoricVersions.Count)); + Assert.AreEqual(0, report.Count); + }); + } + + [Test] + [AutoMoqData] + public void PerformContentVersionCleanup_Always_FiresDeletedVersionsForEachDeletedVersion( + [Frozen] Mock eventAggregator, + [Frozen] Mock policy, + [Frozen] Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentVersionService sut) + { + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + eventAggregator + .Setup(x => x.PublishCancelable(It.IsAny())) + .Returns(false); + + policy.Setup(x => x.Apply(aDateTime, someHistoricVersions)) + .Returns(someHistoricVersions); + + // # Act + sut.PerformContentVersionCleanup(aDateTime); + + eventAggregator.Verify(x => x.Publish(It.IsAny()), Times.Exactly(someHistoricVersions.Count)); + } + + [Test, AutoMoqData] + public void PerformContentVersionCleanup_Always_ReturnsReportOfDeletedItems( + [Frozen] Mock eventAggregator, + [Frozen] Mock policy, + [Frozen] Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentVersionService sut) + { + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + eventAggregator + .Setup(x => x.PublishCancelable(It.IsAny())) + .Returns(false); + + // # Act + var report = sut.PerformContentVersionCleanup(aDateTime); + + Assert.Multiple(() => + { + Assert.Greater(report.Count, 0); + Assert.AreEqual(someHistoricVersions.Count, report.Count); + }); + } + + [Test, AutoMoqData] + public void PerformContentVersionCleanup_Always_AdheresToCleanupPolicy( + [Frozen] Mock eventAggregator, + [Frozen] Mock policy, + [Frozen] Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentVersionService sut) + { + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + eventAggregator + .Setup(x => x.PublishCancelable(It.IsAny())) + .Returns(false); + + policy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) + .Returns>((_, items) => items.Take(1)); + + // # Act + var report = sut.PerformContentVersionCleanup(aDateTime); + + Debug.Assert(someHistoricVersions.Count > 1); + + Assert.Multiple(() => + { + policy.Verify(x => x.Apply(aDateTime, someHistoricVersions), Times.Once); + Assert.AreEqual(someHistoricVersions.First(), report.Single()); + }); + } + + /// + /// For v9 this just needs a rewrite, no static events, no service location etc + /// + [Test, AutoMoqData] + public void PerformContentVersionCleanup_HasVersionsToDelete_CallsDeleteOnRepositoryWithFilteredSet( + [Frozen] Mock eventAggregator, + [Frozen] Mock policy, + [Frozen] Mock documentVersionRepository, + List someHistoricVersions, + DateTime aDateTime, + ContentVersionService sut) + { + documentVersionRepository.Setup(x => x.GetDocumentVersionsEligibleForCleanup()) + .Returns(someHistoricVersions); + + eventAggregator + .Setup(x => x.PublishCancelable(It.IsAny())) + .Returns(false); + + var filteredSet = someHistoricVersions.Take(1); + + policy.Setup(x => x.Apply(It.IsAny(), It.IsAny>())) + .Returns>((_, items) => filteredSet); + + // # Act + var report = sut.PerformContentVersionCleanup(aDateTime); + + Debug.Assert(someHistoricVersions.Any()); + + var expectedId = filteredSet.First().VersionId; + + documentVersionRepository.Verify(x => x.DeleteVersions(It.Is>(y => y.Single() == expectedId)), Times.Once); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs index d1ce62242e..da5175f272 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs @@ -121,6 +121,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers Mock.Get(umbracoMembersUserManager) .Setup(x => x.ValidatePasswordAsync(It.IsAny())) .ReturnsAsync(() => IdentityResult.Success); + Mock.Get(umbracoMembersUserManager) + .Setup(x => x.GetRolesAsync(It.IsAny())) + .ReturnsAsync(() => Array.Empty()); Mock.Get(memberTypeService).Setup(x => x.GetDefault()).Returns("fakeAlias"); Mock.Get(backOfficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(backOfficeSecurity); Mock.Get(memberService).SetupSequence( @@ -162,6 +165,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers Mock.Get(umbracoMembersUserManager) .Setup(x => x.ValidatePasswordAsync(It.IsAny())) .ReturnsAsync(() => IdentityResult.Success); + Mock.Get(umbracoMembersUserManager) + .Setup(x => x.GetRolesAsync(It.IsAny())) + .ReturnsAsync(() => Array.Empty()); Mock.Get(memberTypeService).Setup(x => x.GetDefault()).Returns("fakeAlias"); Mock.Get(backOfficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(backOfficeSecurity); Mock.Get(memberService).SetupSequence( @@ -209,6 +215,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers Mock.Get(umbracoMembersUserManager) .Setup(x => x.UpdateAsync(It.IsAny())) .ReturnsAsync(() => IdentityResult.Success); + Mock.Get(umbracoMembersUserManager) + .Setup(x => x.GetRolesAsync(It.IsAny())) + .ReturnsAsync(() => Array.Empty()); Mock.Get(memberTypeService).Setup(x => x.GetDefault()).Returns("fakeAlias"); Mock.Get(globalSettings); @@ -392,6 +401,10 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers Mock.Get(umbracoMembersUserManager) .Setup(x => x.AddToRolesAsync(It.IsAny(), It.IsAny>())) .ReturnsAsync(() => IdentityResult.Success); + Mock.Get(umbracoMembersUserManager) + .Setup(x => x.GetRolesAsync(It.IsAny())) + .ReturnsAsync(() => Array.Empty()); + Mock.Get(memberTypeService).Setup(x => x.GetDefault()).Returns("fakeAlias"); Mock.Get(backOfficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(backOfficeSecurity); Mock.Get(memberService).Setup(x => x.GetByUsername(It.IsAny())).Returns(() => member); @@ -416,6 +429,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers .Verify(u => u.GetRolesAsync(membersIdentityUser)); Mock.Get(umbracoMembersUserManager) .Verify(u => u.AddToRolesAsync(membersIdentityUser, new[] { roleName })); + Mock.Get(umbracoMembersUserManager) + .Verify(x => x.GetRolesAsync(It.IsAny())); Mock.Get(memberService) .Verify(m => m.Save(It.IsAny())); AssertMemberDisplayPropertiesAreEqual(memberDisplay, result.Value);