C# unit test mocking DbSet errors with IQueryable not implementing IAsyncEnumerable - c#

I have the test below and am using standard mocking on a DbSet/Context. When the test runs it fails as it states that "The source IQueryable doesn't implement IAsyncEnumerable Team. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations".
public async Task Get_team_name_with_valid_search_term_returns_team_names()
{
// Arrange
var data = new List<Team>
{
new Team {Name = "Leeds"},
new Team {Name = "Glasgow"}
}.AsQueryable();
var mockSet = new Mock<DbSet<Team>>();
mockSet.As<IQueryable<Team>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Team>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Team>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Team>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<RadContext>();
mockContext.Setup(c => c.Team).Returns(mockSet.Object);
var service = new ITeamSearchService(mockContext.Object);
// Act
var result = await service.GetTeamName("Gla");
// Assert
}
The service itself is quite simple
public async Task<List<SearchTeamResponse>> GetTeamName(string searchTerm)
{
if (searchTerm == null)
{
throw new ArgumentNullException(nameof(searchTerm));
}
var query = await _radContext.Team.Where(x => x.Name.StartsWith(searchTerm))
.OrderBy(x => x.Name)
.ToListAsync();
var json = JsonConvert.SerializeObject(query);
var result = JsonConvert.DeserializeObject<List<SearchTeamResponse>>(json);
return result;
}

Related

Unable To Load View Asynchronously - ASP.NET Core MVC

I am currently working on a website that uses a Redis cache implementation, EF Core, and async/await. I'm running into a strange problem when trying to load a view with a view model from an async controller method.
Controller method:
public async Task<ActionResult> Events()
{
var clientEvents = await _eventService.GetEvents();
var model = new EventsListViewModel(clientEvents.FutureEvents, clientEvents.PastEvents, false) //Change this)
{
ClientName = _tenantService.Client.Name,
HasSearch = clientEvents.SearchConfig.IsDisplayed
};
return View(model);
}
Service method:
public async Task<ClientEvents> GetEvents()
{
var cacheValue = await _distributedCache.GetStringAsync($"{REDIS_EVENTS_PREFIX}{_clientId}");
ClientEvents clientEvents = null;
if (string.IsNullOrWhiteSpace(cacheValue))
{
var config = await _activateDbContext
.ClientEventSearchConfigurations
.AsNoTracking()
.Where(ec => ec.ClientId == _clientId)
.FirstOrDefaultAsync();
var futureEvents = await _activateDbContext
.Events
.AsNoTracking()
.Include(e => e.Venue)
.Include(e => e.EventType)
.Where(e => e.ClientId == _clientId)
.Where(e => e.IsActive)
.Where(e => (e.StartDate <= DateTime.UtcNow && e.EndDate >= DateTime.UtcNow) || e.StartDate >= DateTime.UtcNow)
.OrderBy(e => e.StartDate)
.ToListAsync();
var pastEvents = await _activateDbContext
.Events
.AsNoTracking()
.Include(e => e.Venue)
.Include(e => e.EventType)
.Where(e => e.ClientId == _clientId)
.Where(e => e.IsActive)
.Where(e => e.EndDate < DateTime.UtcNow)
.OrderByDescending(e => e.StartDate)
.Take(5)
.ToListAsync();
clientEvents = new ClientEvents
{
SearchConfig = config,
FutureEvents = futureEvents,
PastEvents = pastEvents
};
var options = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromDays(1));
await _distributedCache.SetStringAsync($"{REDIS_EVENTS_PREFIX}{_clientId}", JsonConvert.SerializeObject(clientEvents, Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}), options);
}
else
{
clientEvents = JsonConvert.DeserializeObject<ClientEvents>(cacheValue);
}
return clientEvents;
}
With this implementation, the return View(model); line in the controller method is showing that the model has been properly populated, but once that line is ran, I see this exception:
IFeatureCollection has been disposed. Object name: 'Collection'
Strangely, if I simply change the service method to use
var cacheValue = _distributedCache.GetString($"{REDIS_EVENTS_PREFIX}{_clientId}");
everything works as expected. I'm banging my head into my desk trying to figure this out, any help is appreciated.
EDIT: Adding code from Startup.cs -
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ActivateDBContext>(options =>
options.UseSqlServer(_configuration[ConfigurationConstants.ACTIVATE_DB_CONNECTION_STRING]));
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = _configuration[ConfigurationConstants.REDIS_CONNECTION_STRING];
options.InstanceName = _configuration[ConfigurationConstants.REDIS_INSTANCE_NAME];
});
services.AddRazorPages();
services.AddControllersWithViews();
services.AddDistributedMemoryCache();
services.AddIPStackApi();
services.AddResponseCompression();
services.AddPortableObjectLocalization(options => options.ResourcesPath = "Localization");
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-US"),
new CultureInfo("en"),
new CultureInfo("fr-FR"),
new CultureInfo("fr"),
new CultureInfo("es"),
new CultureInfo("es-ES"),
};
options.DefaultRequestCulture = new RequestCulture("en-US");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
services.AddScoped<ITenantService, TenantService>();
services.AddScoped<ILocationService, LocationService>();
services.AddScoped<IEventService, EventService>();
services.AddScoped<IAncillarySearchService, AncillarySearchService>();
services.AddMvc().AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
}

Cannot get unit test to pass (httpRequest mock )

I wrote a unit test thats failing:
[Test]
public async Task GetLogsPaged_Account_ExpectsSuccess()
{
//Arrange
var request = new Mock<HttpRequest>();
request.Setup(x => x.Scheme).Returns("https");
request.Setup(x => x.Host).Returns(HostString.FromUriComponent("https://localhost:5001"));
request.Setup(x => x.PathBase).Returns(PathString.FromUriComponent("/v1/customersupport-manager/logs/account/D4C88E3C-2848-400F-AB66-0DC3FAFFD24A?PageNumber=1&PageSize=5&DateFrom=2021-09-30%2012%3A30%3A13.413&DateTo=2021-09-30%2012%3A33%3A05.317"));
List<LogEntry> logEntries = new List<LogEntry>() { new LogEntry { Id = 1 }, new LogEntry { Id = 2 } };
PaginationFilter paginationFilter = new PaginationFilter { };
LogEntryListFilter logEntryListFilter = new LogEntryListFilter { };
var accountId = Guid.NewGuid();
LogEntryPageResult logs = new LogEntryPageResult() { PagedRecords = logEntries };
A.CallTo(() => logRepo.GetLogs(A<Guid>._, A<PaginationFilter>._, A<LogEntryListFilter>._)).Returns(logs);
//Act
var result = await svc.GetLogs(accountId, request.Object, paginationFilter, logEntryListFilter);
//Assert
result.ApplicationEvents[0].Code.Should().Be(ApplicationResponseCodes.System.OperationSucceed);
}
When I debug the test I get a exception that says - {"Object reference not set to an instance of an object."} System.Exception {System.NullReferenceException}.
Which can only be var request because it's mocked. I'm not sure why I'm getting this exception when I've seen HttpRequest being mocked in other tests. Any assistance is appreciated.
Edit: I'm including the the line that fails in my code
var uri = new Uri(request.GetDisplayUrl()); <- System.NullReference
But I have the url specified in my tests :
request.Setup(x => x.Scheme).Returns("https");
request.Setup(x => x.Host).Returns(HostString.FromUriComponent("https://localhost:5001"));
request.Setup(x => x.PathBase).Returns(PathString.FromUriComponent("/v1/customersupport-manager/logs/account/D4C88E3C-2848-400F-AB66-0DC3FAFFD24A?PageNumber=1&PageSize=5"));

Callback of Moq is not called?

In the following code. The Assert.Equal(...) of the Callback() is never called?
var test = "Test";
var command = new MyCommand { V = test };
var mock = new Mock<IRepository>(); // IRepository has the method of Save()
var p = new P(test);
mock.Setup(x => x.Save(p))
.Callback<P>(x => Assert.Equal(x.Value, test)); // break point on Assert.Equal not hit
var sut = new C(mock.Object);
var result = await sut.M(command);
You have:
.Setup(x => x.Save(p))
but are you sure the P used in your SUT is "equal to" just that p? Instead you could do:
.Setup(x => x.Save(It.IsAny<P>()))
and in that case the set-up (and call-back) would apply to any argument.

FakeitEasy - EntityFramework

I am writing an unit test to test my EF project . while doing the test I am getting an error while configuraing the fake for the DBSet.
My Call stack is
System.NotImplementedException
The member 'IQueryable.ElementType' has not been implemented on type
'DbSet`1Proxy_10' which inherits from 'DbSet`1'. Test doubles for 'DbSet`1'
must provide implementations of methods and properties that are used.
at
system.Data.Entity.Infrastructure.DbQuery`1.GetInternalQueryWithCheck(String
memberName)
at
My Unit Test Code is - I had added a mapping data
var socMappingList = new List<DFC_SocMappings>()
{
new DFC_SocMappings()
{
JobProfile = "Police Officer",
ONetCode = "111-00.01",
QualityRating = 4,
SocCode = "1120"
},
new DFC_SocMappings()
{
JobProfile = "Police Officer",
ONetCode = "111-00.02",
QualityRating = 4,
SocCode = "1121"
},
};
//Arrange
var applicationLogger = A.Fake<IApplicationLogger>();
var fakeIQueryable = new List<DFC_SocMappings>().AsQueryable();
var context = A.Fake<IObjectContextFactory<OnetRepositoryDbContext>>();
// var fakeDbSet = A.Fake<DbSet<DFC_SocMappings>>();
IMapper iMapper = new AutoMapper.Mapper(new MapperConfiguration(cfg => cfg.AddProfile(new SkillsFrameworkMapper())));
var fakeDbSet = A.Fake<DbSet<DFC_SocMappings>>((d => d.Implements(typeof(IQueryable<DFC_SocMappings>)).Implements(typeof(IDbAsyncEnumerable<DFC_SocMappings>)))).SetupData(socMappingList);
A.CallTo(() => context.GetContext().DFC_SocMappings).Returns(fakeDbSet);
var ct = A.Fake<OnetRepository>(op => op.WithArgumentsForConstructor(new object[] { context, iMapper, applicationLogger }));
//Act
A.CallTo(() => ((IQueryable<DFC_SocMappings>)fakeDbSet).Provider).Returns(fakeIQueryable.Provider);
A.CallTo(() => ((IQueryable<DFC_SocMappings>)fakeDbSet).Expression).Returns(fakeIQueryable.Expression);
A.CallTo(() => ((IQueryable<DFC_SocMappings>)fakeDbSet).ElementType).Returns(fakeIQueryable.ElementType);
A.CallTo(() => ((IQueryable<DFC_SocMappings>)fakeDbSet).GetEnumerator()).Returns(fakeIQueryable.GetEnumerator());
//Assert
var onetRespository = new OnetRepository(context, iMapper, applicationLogger);
var response = await onetRespository.GetAllSocMappingsAsync<DfcOnetSocMappings>();
var res = response; // failing while reading the data
A.CallTo(() => context.GetContext().Set<DFC_SocMappings>()).MustHaveHappened();
response.Should().NotBeNull();
response.Should().BeSameAs(socMappings);
onetRespository.Dispose();
Can someone tell what mistake I am doing.

How do I get this mock to work?

I need to make changes to a legacy class with no tests so I have started by writing a test, however the mocking (using Moq) isn't working properly.
This my test
[Test]
public void CorrectlyCopiesToLightningWhenNoLocationsExist()
{
// arrange
long orgId = Id64.NewId();
var data = LightningMapLocationsHelperTestData.ForNormalCopy(orgId);
var organisation = new Group
{
GroupId = orgId,
Name = "Test Organisation",
Type = GroupType.OrganisationGroup
};
var groupRepo = new Mock<IGroupRepository>();
groupRepo.Setup(r => r.GetGroup(orgId)).Returns(organisation);
var orgRepo = Mock.Of<IOrganisationRepository>(o => o.LightningLocationsEnabledFor(orgId));
var mapLocationRepo = new Mock<IMapLocationRepository>();
mapLocationRepo.Setup(r => r.OrganisationRepository).Returns(orgRepo);
mapLocationRepo
.Setup(r => r.GetMapLocationsByGroupIds(orgId, It.IsAny<IEnumerable<long>>(), true, true))
.Returns(data.InitialDatabaseLocations);
var lightningMapLocationRepo = new Mock<ILightningMapLocationRepository>();
lightningMapLocationRepo
.Setup(r => r.LocationsById(orgId, data.InitialLightningLocations.Select(l => l.LocationId)))
.Returns(data.InitialLightningLocations);
lightningMapLocationRepo
.Setup(r => r.AddLocations(It.IsAny<List<Location>>()))
.Callback((List<Location> locations) => data.InitialLightningLocations.AddRange(locations));
var infoMessages = new List<string>();
var errorMessages = new List<string>();
var helper = new LightningMapLocationsHelper(
(string s, object[] args) => infoMessages.Add(string.Format(s, args)),
(string s, object[] args) => errorMessages.Add(string.Format(s, args)));
List<CopyFailure> copyFailures;
// act
bool success = helper.CopyLocationsForOrganisation(orgId, 10, out copyFailures);
// assert
success.ShouldBeTrue();
(errorMessages?.Count ?? 0).ShouldBe(0);
data.InitialLightningLocations.Count.ShouldBe(data.ExpectedLightningLocations.Count);
}
Inside LightningMapLocationsHelper is the following method
private Group GetValidOrganisationGroup(long groupId)
{
var organisation = (new GroupRepository()).GetGroup(groupId);
if (organisation != null && organisation.Type == GroupType.OrganisationGroup) return organisation;
LogErrorMessage("Invalid groupId: {0}. Ignoring...", groupId);
return null;
}
that when called is using an actual instance of GroupRepository rather than the groupRepo mock set up in the test, thus causing the test to fail. As GroupRepository implements IGroupRepository I expected this to work.
public class GroupRepository : IGroupRepository {…}
Perhaps I am misunderstanding how mocking works. Can someone offer some insight to help me understand why this doesn't work, and how I can fix it? Do I have to pass the mocked classes in?

Categories