Async lambda expressions in Xunit Assert.Throws - c#

I have some test code asserting duplicate Users cannot be created through my UserRepository.
User.cs:
public class User
{
public int Id { get; set; }
public string AccountAlias { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
}
UserRepository.cs:
public class UserRepository
{
public virtual async Task<User> CreateAsync(User entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
if (await GetDuplicateAsync(entity) != null)
{
throw new InvalidOperationException("This user already exists");
}
return Create(entity);
}
public async Task<User> GetDuplicateAsync(User user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
return await (from u in Users
where u.AccountAlias == user.AccountAlias &&
u.Id != user.Id &&
u.IsActive
select u).FirstOrDefaultAsync();
}
}
UserRepositoryTests.cs:
public sealed class UserRepositoryTests : IDisposable
{
public UserRepositoryTests()
{
UserRepository = new UserRepository(new FooEntities()); // DbContext
// from EF
}
private UserRepository UserRepository { get; set; }
[Fact]
public void DuplicateUserCannotBeCreated()
{
var testUser = new User // This test user already exists in database
{
Id = 0,
AccountAlias = "domain\\foo",
DisplayName = "Foo",
Email = "foo#bar.com",
IsActive = true
};
Assert.Throws<InvalidOperationException>(async () =>
await UserRepository.CreateAsync(testUser));
}
public void Dispose()
{
if (UserRepository != null)
{
UserRepository.Dispose();
}
}
}
When I run this unit test, Xunit.Sdk.ThrowsException is thrown (i.e. my InvalidOperationException was not thrown):
Assert.Throws() Failure
Expected: System.InvalidOperationException
Actual: (No exception was thrown)
From the debugger, GetDuplicateAsync() was evaluated but when the LINQ query was executed, the result was never returned and thus no exception was thrown. Can anyone help?

xUnit's Assert.Throws (at least on version 1.9.2) is not async-aware. This was fixed in version 2, which now has an Assert.ThrowsAsync method.
So, you can either upgrade to xUnit 2 or create your own method to get it working:
public async static Task<T> ThrowsAsync<T>(Func<Task> testCode) where T : Exception
{
try
{
await testCode();
Assert.Throws<T>(() => { }); // Use xUnit's default behavior.
}
catch (T exception)
{
return exception;
}
return null;
}
await ThrowsAsync<InvalidOperationException>(async () => await UserRepository.CreateAsync(testUser));
From Haacked's gist.

XUnit now handle Assert.ThrowAsync by default

This works for me:
Assert.Throws<AbpValidationException>(() => _personAppService.CreatePersonAsync(new CreatePersonInput { Name = null }));
Just don't use async/await.

Related

Handling User tries to add a new user with a duplicate id error in c#

I'm trying to Create user defined function that handling User tries to add a new user with a duplicate id
and I create this :
public class DuplicateIdException:Exception
{
public DuplicateIdException(String message) : base(message)
{
}
public override string Message => $" --- {base.Message} --- ";
}
public class TestDuplicateIdException
{
static void validate(List<Object> Users)
{
bool flag = false;
Type DataType = Users[0].GetType();
List<DataType> UsersConverter = Users.Cast<DataType>().ToList();
flag = UsersConverter.Any(x => x.Id == Id);
if (flag)
{
throw new DuplicateIdException("Sorry, You duplicate the Id");
}
}
}
I have many objects types and all of them have Id attribute in them, but when i call object.Id it gave an error and not working.. So how can i check them and complete the Exception ?
You can create a base class for all classes that have id ,it can be an interface or base class if you need to.
public class EntityBase
{
public int Id { get; set; }
}
then inherit/implement it
public class Users : EntityBase
{
}
public class Order : EntityBase
{
}
then validate with the base class/interface
public class ValidateDuplidateId
{
static void Validate(IEnumerable<EntityBase> entities,int id)
{
if (entities.Any(x => x.Id == id))
throw new Exception("Duplicate Id Found");
}
}
i don't like this approach of throwing exceptions for handling business logic errors, a better approach for me will be something like this
public class Validator
{
public static bool IsDuplicateId(IEnumerable<EntityBase> entities,int id)
{
if (entities.Any(x => x.Id == id))
return true;
return false;
}
}
then i will add a generic operation result class to handle any type of objects
public class OperationReuslt<T>
{
public T Result { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
}
in my user service ( for example )
public class UserSerivce
{
public OperationReuslt<Users>AddUser(int id)
{
//replace this with data from your actual data source
List<Users> users = new List<Users>();
if(Validator.IsDuplicateId(users,id))
{
return new OperationReuslt<Users>
{
Success = false,
Message = "Duplicate UserId"
};
}
// else proceed
}
}
you can use this approach as it more readable and doesn't have a performance drawback as the throwing exception approach , but in the end it all depends on your case

How to write testcases using Xunit test for extension method like Any

Actually this is my service layer. I want to test a create account method by Xunit testing. How can I proceed further?
public class UserService :IUserService // service
{
#region Property
private readonly IAppDbContext _appDbContext;
#endregion
#region Constructor
public UserService(IAppDbContext appDbContext)
{
_appDbContext = appDbContext;
}
#endregion
public int Create(User model)
{
_appDbContext.Users.Add(model);
_appDbContext.SaveChanges();
return model.Id;
}
public bool CheckAccount(User data)
{
if (this._appDbContext.Users.Any(x => x.UserName == data.UserName))
{
return false;
}
else
{
return true;
}
}
public string CheckDetails(User data)
{
if (this._appDbContext.Users.Any(x => x.Password == data.Password) &&
this._appDbContext.Users.Any(x => x.UserName == data.UserName))
{
var userid = "";
var obj = this._appDbContext.Users.Where(x => x.UserName == data.UserName);
foreach (var i in obj)
{
userid = i.Id.ToString();
}
return userid;
}
else
{
return null;
}
}
}
}
Please tell ,how to I test this method are there in User service by xunit testing
That's simple. You need to use [Theory] and [InlineData] XUnit attributes.
I prepared a small example for you
public class UnitTest
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
[Theory]
[InlineData("First")]
public void Test1(string productName)
{
var productList = new List<Product>
{
new()
{
Id = 1,
Name = "First"
},
new()
{
Id = 2,
Name = "Second"
}
};
Assert.True(productList.Any(product => product.Name == productName));
}
}

ASP .Net core Identity Framework, user can't be added and no error message

I'm trying to add a user in my database but when I create it it is not added and it doesn't give me an error message. When I add a breakpoint to the CeateUser method the code is running but when I add a breakpoint on the result condition, the code is never reached.
Here's my PageModel
public class AddUserModel : PageModel
{
[BindProperty]
public NewAccountInput AccountInput { get; set; }
private UserManager<AdminUser> UserManager { get; set; }
public AddUserModel(UserManager<AdminUser> userManager)
{
UserManager = userManager;
}
public void OnPost()
{
if(AccountInput.ComfirmationPassword != AccountInput.Password)
{
return;
}
_ = CreateUser();
}
public async Task<bool> CreateUser()
{
var result = await UserManager.CreateAsync(new AdminUser()
{
Email = AccountInput.Email,
FirstName = AccountInput.UserName,
LastName = AccountInput.UserName,
UserName = AccountInput.Email
}, AccountInput.Password);
if (result.Succeeded)
{
return true;
}
else
{
return false;
}
}
}
The solution is because the method OnPost must be of type Task.
public async Task OnPost()
{
if(AccountInput.ComfirmationPassword != AccountInput.Password)
{
return;
}
await CreateUser();
}

cannot convert from 'System.Collections.Generic.ICollection<x>' to 'x'

i need to add some data in OptionRoleTable:
public class OptionRole
{
public int Id { get; set; }
public int RoleId { get; set; }
public int OptionsId { get; set; }
public virtual Role Role { get; set; }
public virtual Options Options { get; set; }
}
and this is Options Tabel:
public partial class Options
{
public int Id { get; set; }
public string OptionName { get; set; }
public string RouteFunctionName { get; set; }
public string Icon { get; set; }
public virtual ICollection<OptionRole> OptionRoles { get; set; }
}
i must check data not exist in OptionRole , when i using this code for add data in OptionRole :
public async Task<Options> findOptionsId(int optionId)
{
return await _option.FirstOrDefaultAsync(x => x.Id == optionId);
}
public async Task<bool> AddorUpdateOptions(int optionId, IList<int> selectedRoleValue)
{
List<OptionVM> optionVMs = new List<OptionVM>();
List<int> currentOptionValue = new List<int>();
var optionRole = await findOptionsId(optionId);
if (optionRole == null)
{
return false;
}
foreach (var item in selectedRoleValue)
{
var findRole = await _roleManager.FindByIdAsync(item);
var findOPR = optionRole.OptionRoles.FirstOrDefault(x => x.OptionsId== optionId && x.RoleId==item);
if (findOPR != null)
{
currentOptionValue.Add(item);
}
}
if (selectedRoleValue == null)
{
selectedRoleValue = new List<int>();
}
var newOptionRole = selectedRoleValue.Except(currentOptionValue).ToList();
foreach (var opRole in newOptionRole)
{
var findRole = await _roleManager.FindByIdAsync(opRole);
if (findRole != null)
{
optionRole.OptionRoles.Add(new OptionRole
{
OptionsId = optionRole.Id,
RoleId = findRole.Id
});
}
}
var removeOptionRole = currentOptionValue.Except(selectedRoleValue).ToList();
foreach (var remove in removeOptionRole)
{
var findOptionRole = _optionRoles.FirstOrDefault(x => x.Id == remove);
if (findOptionRole != null)
{
optionRole.OptionRoles.Remove(findOptionRole);
}
}
return Update(optionRole.OptionRoles);
}
I must have pass a class type of Options when i using this code . it show me this Error :
Severity Code Description Project File Line Suppression State
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.ICollection' to 'StoreFinal.Entities.Entities.Identity.OptionRole' StoreFinal.Services C:\Users\Mr-Programer\Desktop\New folder\StoreFinal\StoreFinal.Services\Contracts\Identity\Service\ApplicationOptionRoleManager.cs 97 Active
Error in this line : return Update(optionRole.OptionRoles);
whats the problem ? how can i solve this problem ?
Edit :
Update Method :
public virtual bool Update(T entity)
{
try
{
Entities.Attach(entity);
return true;
}
catch (Exception)
{
return false;
}
}
Look at the Update Method signature:
public virtual bool Update(T entity);
It accepts a param type T which should be One Entity - Why One Entity -- because Entities.Attach() accepts only 1 Object. While what you are passing to it is:
return Update(optionRole.OptionRoles);
Where OptionRoles is of type: ICollection<OptionRole> --
For understandings sake, Change it to
return Update(optionRole.OptionRoles[0]);
or
return Update(optionRole.OptionRoles.First());
And then share the result.

Entity Framework not updating entity when using Task.Factory

I have Entity Framework code which is supposed to be updating an entity called Asset. For some reason, it's not updating as it should. Debugging to see the root cause of this issue is difficult as I don't see any exceptions.
The problem is in the repository class where it always returns an
Asset could not be updated
message even if I pass in a valid Book or Asset object.
Model classes
public class Asset
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Book : Asset
{
public string Isbn { get; set; }
public string Edition { get; set; }
public string Publisher { get; set; }
public virtual User User { get; set; }
}
Controller class
[System.Web.Http.Authorize]
public async Task<IHttpActionResult> Put([FromBody] BookViewModel model)
{
string result = null;
try
{
if (model.IsEmpty)
return BadRequest(ModelState);
result = await _assetAssetRepository.Update(Mapper.Map<Book>(model));
return Content(HttpStatusCode.Created, result);
}
catch (Exception ex)
{
return Content(HttpStatusCode.InternalServerError, result);
}
}
Repository class:
public class AssetRepository
{
private readonly GenAppContext _context = new GenAppContext();
public Task<string> Update(Asset asset)
{
try
{
var updateTask = Task<string>.Factory.StartNew(() =>
{
if (!(asset is Book))
return "Please return correct type of asset";
var _asset = _context.Assets.SingleOrDefault(x => x.Id == asset.Id);
if (_asset == null)
throw new ArgumentNullException(nameof(_asset));
_asset.Name = asset.Name;
((Book) _asset).Edition = ((Book) asset).Edition;
((Book) _asset).Publisher = ((Book) asset).Publisher;
((Book) _asset).Isbn = ((Book) asset).Isbn;
_context.Assets.AddOrUpdate(_asset);
return _context.SaveChanges() > 0
? "Asset has been updated successfully"
: "Asset could not be updated";
});
return updateTask;
}
catch (Exception ex)
{
return Task<string>.Factory.StartNew(() => ex.Message + " " + ex.StackTrace);
}
}
}

Categories