The method I'm testing has to throw a custom "ApiNotFoundException" when the searched entity is not found. I have no problems testing that as I can just write:
await Assert.ThrowsAsync<ApiNotFoundException>(async () => await _mockAccountHoldersRepository.Object.GetAccountHoldersList(_accountid.ToString(), true));
But the problem is that "GetAccountHoldersList" calls a static method in a class which doesn't have an interface.
I cannot modify the methods, only the tests, so I'm forced to use Pose (or Microsoft's "Fake") in order to Mock the static method and fake a result.
To test:
public Task<IQueryable<AccountHolder>> GetAccountHoldersList(string accountId, bool expandHolderType)
{
var _accountId = UidResolver.MapUidToId(accountId); //=> static method
var queryresult = _context
.AccountHolders
.Include(a => a.Account).AsQueryable();
if (expandHolderType)
{
queryresult = queryresult.Include(ht => ht.HolderType).AsQueryable();
}
queryresult = queryresult.Where(ah => ah.AccountId == _accountId && ah.EndDate == null).AsQueryable();
if ((!queryresult.Any()) || (accountId == null))
{
//=>This is the throw I need to test
throw new ApiNotFoundException(ErrorString.AccountHoldersRepository.GetAccountHolders_NotFound, new { accountId });
}
return Task.FromResult(queryresult);
}
UidResolver:
public abstract partial class UidResolver
{
protected static UidResolver Default { get; set; }
public static long MapUidToId(string guid)
{
// Trivial or null case
if (String.IsNullOrEmpty(guid))
{
return 0;
}
if (!Ids.TryGetValue(guid, out var value))
{
if (!Options.RefreshIfNotFound)
{
throw new ApiNotFoundException($"MapUuidToId: Key {guid} not found.");
}
if (Default?.TryRefreshUid(guid, out value) != true)
{
if (Options.ThrowNotFoundException)
{
throw new ApiNotFoundException($"MapUuidToId: Key {guid} not found.");
}
else
{
value = 0;
}
}
}
return value;
}
}
Test method:
[Fact]
public async Task GetAccountList_ThrowsException_WhenNoData()
{
_accountid = Guid.NewGuid();
var acountHolders = new List<AccountHolder>();
_mockDbContext
.Setup(x => x.AccountHolders)
.Returns(ApiDbContextMock.GetMockDbSet(acountHolders));
//var cache = _mockDistributedCacheGrowth.Object;
//UidResolver.AddUid(_accountid, 1, cache);
Shim shim = Shim.Replace(() => UidResolver.MapUidToId(Is.A<string>()))
.With(delegate (string guid) { return (long)1; });
var actualResult = new List<AccountHolder>();
PoseContext.Isolate(async () =>
{
await Assert.ThrowsAsync<ApiNotFoundException>(async () => await _mockAccountHoldersRepository.Object.GetAccountHoldersList(_accountid.ToString(), true));
}, shim);
}
If I uncomment the two commented lines, testing works just fine (and I don't even need "Pose"), but I don't think it's a good idea to call the real method (and not a mocked one) during a test.
This line:
Shim shim = Shim.Replace(() => UidResolver.MapUidToId(Is.A<string>()))
.With(delegate (string guid) { return (long)1; });
it's working fine (or at least not crashing), but the problem is I don't know then how to construct the
PoseContext.Isolate(async () => {});
part.