how to test IQueryable of strongly typed using xUnit in .NET CORE - asp.net-web-api

I am working on a test project for .NET CORE Web API project. I have SchoolService class that implements numbers of methods as some of them below
Service Class
public class SchoolService : ISchoolService
{
private readonly ISchoolEntity schoolEntity;
public SchoolService(ISchoolEntity schoolEntity)
{
this.schoolEntity = schoolEntity;
}
public IQueryable<SchoolDataView> GetAllSchools()
{
var query = this.schoolEntity.GetAllSchool();
return query;
}
public SchoolDataView GetSchoolById(Guid Id)
{
var query = this.schoolEntity.GetSchoolById(Id);
return query;
}
I want to test
1- GetAllSchools return object type is of IQueryable?
2- How I use autofix or by another way for schoolEntity.GetAllSchool() return fake IQueryable?
Service Test
public class SchoolServiceTests
{
private readonly ISchoolService schoolService;
private readonly ISchoolEntity schoolEntity = Substitute.For<ISchoolEntity>();
public SchoolServiceTests()
{
schoolService = new SchoolService(schoolEntity);
}
[Fact]
public void GetAllSchool_ShouldReturn_IQueryableOfSchoolDataView()
{
//Arrange
//Act
var a = schoolEntity.GetAllSchool();
//Assert
Assert.??
}
}

I have written following test to achieve behaviour that I have stated in my question. Open to hearing more feedback and suggestions on it. Thanks
[Fact]
public void GetAllSchool_ShouldReturn_IQueryableOfSchoolDataView()
{
//Arrange
var schoolDataViewList = new List<SchoolDataView>
{
new SchoolDataView { SchoolID = Guid.NewGuid(), Name = "London Primary School"},
new SchoolDataView { SchoolID = Guid.NewGuid(), Name = "Windsor Gramer School"},
new SchoolDataView { SchoolID = Guid.NewGuid(), Name = "Kings College"},
new SchoolDataView { SchoolID = Guid.NewGuid(), Name = "Reading School"}
}.AsQueryable();
schoolEntity.GetAllSchool().Returns(schoolDataViewList);
//Act
var actualSchoolList = sut.GetAllSchools();
//Assert
Assert.IsAssignableFrom<IQueryable<SchoolDataView>>(actualSchoolList);
}
OR Using AutoFixture
[Fact]
public void GetAllSchool_ShouldReturn_IQueryableOfSchoolDataView()
{
//Arrange
var fixture = new Fixture();
var schoolDataViewMock = fixture.CreateMany<SchoolDataView>();
schoolEntity.GetAllSchool().Returns(schoolDataViewMock.AsQueryable());
//Act
var actualSchoolDataList = sut.GetAllSchools();
//Assert
Assert.IsAssignableFrom<IQueryable<SchoolDataView>>(actualSchoolDataList);
}

Related

What will be the xUnit test case for Create method in ASP.NET Core MVC?

Here is my code to create a new record in ASP.NET Core MVC application.
Code: (CreateController.cs file)
[HttpPost]
public async Task<IActionResult> Add(AddEmployeeViewModel addEmp)
{
var employee = new Employee()
{
Id = Guid.NewGuid(),
Name = addEmp.Name,
Email = addEmp.Email,
Salary = addEmp.Salary,
};
await mvcDemoDbContext.Employees.AddAsync(employee);
await mvcDemoDbContext.SaveChangesAsync();
return RedirectToAction("Index");
}
How to write xUnit test case for this particular method?
I have a created a separate xUnit Test project and create a controller instance in Arrange block.
Thanks for your help!
I tried as below followed the two documents: doc1,doc2,hopes could help
public class UnitTest1
{
[Fact]
public async Task TestFor_RedirecForTestAction_WhenModelStateIsValid()
{
//Arrange
var employee = new Employee() { Id = 1, Name = "SomeName" };
var mockset = new Mock<DbSet<Employee>>();
var mockcontext = new Mock<MVCProjForTestContext>(new DbContextOptions<MVCProjForTestContext>());
mockcontext.Setup(x=>x.Employee).Returns(mockset.Object);
var controller = new HomeController(mockcontext.Object);
//Act
var result=await controller.RedirecForTest(employee);
//Asssert
mockset.Verify(x => x.AddAsync(It.IsAny<Employee>(), default(CancellationToken)), Times.Once());
mockcontext.Verify(x=>x.SaveChangesAsync(default(CancellationToken)), Times.Once()) ;
var redirectresult =Assert.IsType<RedirectToActionResult>(result);
Assert.Null( redirectresult.ControllerName);
Assert.Equal("Index", redirectresult.ActionName);
}
}
public class HomeController : Controller
{
private readonly MVCProjForTestContext _context;
public HomeController(MVCProjForTestContext context)
{
_context=context;
}
public async Task<IActionResult> RedirecForTest(Employee employee)
{
if (ModelState.IsValid)
{
await _context.Employee.AddAsync(employee);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
else
{
return BadRequest();
}
}
}
public class MVCProjForTestContext : DbContext
{
public MVCProjForTestContext (DbContextOptions<MVCProjForTestContext> options)
: base(options)
{
}
public virtual DbSet<MVCProjForTest.Models.Employee> Employee { get; set; } = default!;
}
The result:

how to assertThat using mockito

I have created a test case for the below method. How can I use assertThat?
public List<String> getPersonWithMissingTranInfo(){
List<String> person = new ArrayList<>();
for (PersonData personData: personDataHandler.getPersons()){
if (getPersonIranInfos(person.getUniqueId()).size()==0){
person.add(personData.getLocator());
}
}
#Test
public void testGetPersonWithMissingTranIn() {
CtrData ctrData = spy(CrdDataFixture.newCtrData().withDefaultValues());
List<PersonIranInfosData> personIranInfosData = Arrays.asList(mock(PersonIranInfosData.class));
List<String> actual = ctrData.getPersonsWithMissingTranInfo();
//assertThat(actual);
}
}

Cannot seed database with data

I wrote a class to seed the database with data. When debugging it it stacks on await IdentitySeedData.EnsurePopulated(service); Probably the problem is with usermanager.createasync(). Take a look:
namespace SportStore1
{
public class Program
{
public static void Main(string[] args)
{
var Host = BuildWebHost(args);
var Scopes = Host.Services.GetRequiredService<IServiceScopeFactory>();
using (var scope = Scopes.CreateScope())
{
var service = scope.ServiceProvider;
SeedDataFunc(service);
SeedIdentityDataFunc(service);
}
Host.Run();
}
public static void SeedDataFunc(IServiceProvider service)
{
SeedData.EnsurePopulated(service);
}
public static async void SeedIdentityDataFunc(IServiceProvider service)
{
await IdentitySeedData.EnsurePopulated(service);
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
/*
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
*/
}
}
namespace SportStore1.Data
{
public static class IdentitySeedData
{
public static async Task EnsurePopulated(IServiceProvider service)
{
UserManager<IdentityUser> userManager = service.GetRequiredService<UserManager<IdentityUser>>();
IConfiguration configuration = service.GetRequiredService<IConfiguration>();
ApplicationDbContext dbContext = service.GetRequiredService<ApplicationDbContext>();
if (!dbContext.Roles.Any())
{
foreach (var role in Roles)
{
dbContext.Add(role);
await dbContext.SaveChangesAsync();
}
}
if (!dbContext.Users.Any())
{
var user = new IdentityUser() { UserName = configuration["Email"], Email = configuration["Email"], EmailConfirmed = true };
await userManager.CreateAsync(user, configuration["Password"]);
}
if (!dbContext.UserRoles.Any())
{
var roleID = dbContext.Roles.Where(p => p.Name == "Administrator").FirstOrDefault().Id;
var userID = dbContext.Users.Where(p => p.Email == configuration["Email"]).FirstOrDefault().Id;
var userRole = new IdentityUserRole<string>()
{
RoleId = roleID,
UserId = userID
};
dbContext.UserRoles.Add(userRole);
await dbContext.SaveChangesAsync();
}
}
private static List<IdentityRole> Roles = new List<IdentityRole>()
{
new IdentityRole { Name = "Administrator", NormalizedName = "Administrator", ConcurrencyStamp = Guid.NewGuid().ToString() },
new IdentityRole { Name = "Manager", NormalizedName = "Manager", ConcurrencyStamp = Guid.NewGuid().ToString() },
new IdentityRole { Name = "User", NormalizedName = "User", ConcurrencyStamp = Guid.NewGuid().ToString()}
};
}
}
It stacks on await IdentitySeedData.EnsurePopulated(service) with a message System.ObjectDisposedException: 'Cannot access a disposed object.
Object name: 'UserManager`1'.' Any solution?
You need to await the async method. It seems you're getting stuck on the fact that Main is synchronous, but you can change that. In C# 7.2+ you can actually just have an async main:
public static async Task Main(string[] args)
In lesser versions, you'd just do:
public static void Main(string[] args) =>
MainAsync(args).GetAwaiter().GetResult();
public static async Task MainAsync(string[] args)
{
...
}
The compiler just builds that for you under the hood when you use async Main, anyways. Remember that you'll need to also do await host.RunAsync();, if you do this.
That said, this is not the method for doing database seeding any more. See the documentation.
You also need to use using() statement to ensure that the dbcontext is disposed as soon as it goes out of scope.
I tried your code in a brand new asp.net core 2.0 Individual User Account which uses ApplicationUser and it works well after using using block:
public static class IdentitySeedData
{
public static async Task EnsurePopulated(IServiceProvider service)
{
UserManager<ApplicationUser> userManager = service.GetRequiredService<UserManager<ApplicationUser>>();
IConfiguration configuration = service.GetRequiredService<IConfiguration>();
using (var dbContext = service.GetRequiredService<ApplicationDbContext>())
{
//ApplicationDbContext dbContext = service.GetRequiredService<ApplicationDbContext>();
if (!dbContext.Roles.Any())
{
foreach (var role in Roles)
{
dbContext.Add(role);
await dbContext.SaveChangesAsync();
}
}
if (!dbContext.Users.Any())
{
var user = new ApplicationUser() { UserName = configuration["Email"], Email = configuration["Email"], EmailConfirmed = true };
await userManager.CreateAsync(user, configuration["Password"]);
}
if (!dbContext.UserRoles.Any())
{
var roleID = dbContext.Roles.Where(p => p.Name == "Administrator").FirstOrDefault().Id;
var userID = dbContext.Users.Where(p => p.Email == configuration["Email"]).FirstOrDefault().Id;
var userRole = new IdentityUserRole<string>()
{
RoleId = roleID,
UserId = userID
};
dbContext.UserRoles.Add(userRole);
await dbContext.SaveChangesAsync();
}
}
}
private static List<IdentityRole> Roles = new List<IdentityRole>()
{
new IdentityRole { Name = "Administrator", NormalizedName = "Administrator", ConcurrencyStamp = Guid.NewGuid().ToString() },
new IdentityRole { Name = "Manager", NormalizedName = "Manager", ConcurrencyStamp = Guid.NewGuid().ToString() },
new IdentityRole { Name = "User", NormalizedName = "User", ConcurrencyStamp = Guid.NewGuid().ToString()}
};
}
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
}
Refer to Cannot access a disposed object in ASP.NET Core when injecting DbContext

Web API Serialize/Deserialize Derived types

I have a Web API that returns a list of objects, when the client passes Accept application/json I want my globally registered json formatter to include TypeNameHandling for the derived types during serialization. However this doesn't work and I can't see why this shouldn't work ?
My objects
public class BaseClass
{
public int Id { get; set; }
}
public class SubClass : BaseClass
{
public string SubClassProp { get; set; }
}
public class SubClassA : SubClass
{
public string SubClassAProp { get; set; }
}
public class SubClassB : SubClass
{
public string SubClassBProp { get; set; }
}
WebApiConfig
public static void Register(HttpConfiguration config)
{
var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.NullValueHandling = NullValueHandling.Ignore;
settings.TypeNameHandling = TypeNameHandling.Auto;
}
Web API Controller
public class MyController : ApiController
{
[HttpGet]
public async Task<IList<BaseClass>> GetClasses()
{
return new List<BaseClass>
{
new SubClassA
{
Id = 1,
SubClassProp = "SubClass",
SubClassAProp = "SubClassAProp"
},
new SubClassB
{
Id = 2,
SubClassProp = "SubClass",
SubClassBProp = "SubClassBProp"
}
};
}
}
Call from API Client in same solution
var client = new HttpClient() { BaseAddress = new Uri("uri goes here...")}
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var resp = await client.GetAsync("uri goes here..."));
var jsonContent = await resp.Content.ReadAsStringAsync();
var ListOfClasses = JsonConvert.DeserializeObject<IList<BaseClass>>(jsonContent, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
I'am expecting to get one element which is SubClassA and one that is SubClassB, but both is BaseClass ?
I also want it to be possible to Deserialize json to object in Post method.
And this should be possible for both json and xml

How to unit test modelbinder with ModelMetadata

How do I unit test a custom ModelBinder?
Here's the code.
public class MagicBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var boundModelObject = base.BindModel(controllerContext, bindingContext);
var properties = bindingContext.ModelType.GetProperties().Where(a => a.CanWrite);
foreach (var propertyInfo in properties)
{
object outValue = null;
bindingContext.TryGetValue(propertyInfo.Name, propertyInfo.DeclaringType, out outValue);
propertyInfo.SetValue(boundModelObject, outValue, null);
}
return boundModelObject;
}
}
And here is the test script.
[TestMethod]
public void TestFooBinding()
{
var dict = new ValueProviderDictionary(null)
{
{"Number", new ValueProviderResult("2", "2", null)},
{"Test", new ValueProviderResult("12", "12", null)},
};
var bindingContext = new ModelBindingContext() { ModelName = "foo", ValueProvider = dict};
var target = new MagicBinder();
Foo result = (Foo)target.BindModel(null, bindingContext);
}
public class Foo
{
public int Number { get; set; }
public int Test { get; set; }
}
Problem? In the MagicBinder, bindingContext.Model is null. If I try set it with
bindingContext.Model = new Foo(). I get an exception saying it is deprecated, and I should set the ModelMetadata.
So how do I construct a ModelMetadata? It can't even be mocked.
NOTE: This answer is for ASP.NET on .NET Framework and might be outdated.
Try like this:
[TestMethod]
public void TestFooBinding()
{
// arrange
var formCollection = new NameValueCollection
{
{ "Number", "2" },
{ "Test", "12" },
};
var valueProvider = new NameValueCollectionValueProvider(formCollection, null);
var metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Foo));
var bindingContext = new ModelBindingContext
{
ModelName = "",
ValueProvider = valueProvider,
ModelMetadata = metadata
};
var controllerContext = new ControllerContext();
var sut = new MagicBinder();
// act
Foo actual = (Foo)sut.BindModel(controllerContext, bindingContext);
// assert
// TODO:
}
Incase any of you need this to work for web-api you can use this method which will test's Get Requests, you get the benefit of using the built in provider:
Which will populate the values as the would come in from the web, instead of getting bizarre side effects of creating values that the provider may potentially never return Null etc.
using System;
using System.Globalization;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata.Providers;
using System.Web.Http.ModelBinding;
using System.Web.Http.ValueProviders.Providers;
namespace Apps.API.Web.Tests
{
public class ModelBinderTestRule
{
//This URL is just a place holder for prefixing the query string
public const string MOCK_URL = "http://localhost:8088/";
public TModel BindModelFromGet<TBinder, TModel>(string modelName, string queryString, TBinder binder)
where TBinder : IModelBinder
{
var httpControllerContext = new HttpControllerContext();
httpControllerContext.Request = new HttpRequestMessage(HttpMethod.Get, MOCK_URL + queryString);
var bindingContext = new ModelBindingContext();
var dataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadata = dataProvider.GetMetadataForType(null, typeof(TModel));
var httpActionContext = new HttpActionContext();
httpActionContext.ControllerContext = httpControllerContext;
var provider = new QueryStringValueProvider(httpActionContext, CultureInfo.InvariantCulture);
bindingContext.ModelMetadata = modelMetadata;
bindingContext.ValueProvider = provider;
bindingContext.ModelName = modelName;
if (binder.BindModel(httpActionContext, bindingContext))
{
return (TModel)bindingContext.Model;
}
throw new Exception("Model was not bindable");
}
}
}

Resources