I set up my mock object using Moq like this:
var accountRepositoryMock = new Mock<IGenericRepository<Account>>();
accountRepositoryMock.Setup(r => r.SingleOrDefault(a => a.AccountId == It.IsAny<long>())).Returns(new Account { AccountId = 99999999, Valid = true });
var unitOfWorkMock = new Mock<IUnitOfWork>();
unitOfWorkMock.SetupGet(unitofwork => unitofwork.AccountRepository).Returns(accountRepositoryMock.Object);
Then I pass it to my service like this:
IQuickPayService quickPayService = new QuickPayService(unitOfWorkMock.Object);
Account account = quickPayService.ValidateAccount(accountId);
When I do this in my client code, I am getting account as null
public class QuickPayService : IQuickPayService
{
public QuickPayService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public AccountStatus ValidateAccount(long accountId)
{
var account;
using (_unitOfWork)
{
account = _unitOfWork.AccountRepository.SingleOrDefault(acc => acc.AccountId == 99999999);
}
return account;
}
}
Any ideas on what I am doing wrong?
Assuming that your interface is declared similarly to the following:
public interface IGenericRepository<T>
{
T SingleOrDefault(Func<T, bool> predicate);
}
Specifying a delegate in Setup won't work. See Issue 300: Mocking Method with Delegate as Parameter. I get a NotSupportedException when I tried it.
Instead, try this:
accountRepositoryMock.Setup(r => r.SingleOrDefault(It.IsAny<Func<Account, bool>>()))
.Returns(new Account { AccountId = 99999999, Valid = true });
There's a more detailed example at Moq framework Func<T,T>
Related
I have an app that uses CosmosDb as the database and using AspNetIdentityDocument. When I call var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, false), i get the error Cross partition query is required but disabled. Please set x-ms-documentdb-query-enablecrosspartition to true, specify x-ms-documentdb-partitionkey
void InitializeDocumentClient(DocumentClient client) code attempts to create the container if not there. It works for the creating the container on my CossmosDb emultated store but fails on the Azure store requiring a partition key! My app works on the emulated store!
Program.cs
builder.Services.AddDefaultDocumentClientForIdentity(
builder.Configuration.GetValue<Uri>("DocumentDbClient:EndpointUri"),
builder.Configuration.GetValue<string>("DocumentDbClient:AuthorizationKey"),
afterCreation: InitializeDocumentClient);
builder.Services.AddIdentity<ApplicationUser, DocumentDbIdentityRole>()
.AddDocumentDbStores(options =>
{
options.UserStoreDocumentCollection = "AspNetIdentity";
options.Database = "RNPbooking";
})
.AddDefaultTokenProviders();
void InitializeDocumentClient(DocumentClient client)
{
try
{
var db = client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri("RNPbooking")).Result;
}
catch (AggregateException ae)
{
ae.Handle(ex =>
{
if (ex.GetType() == typeof(DocumentClientException) && ((DocumentClientException)ex).StatusCode == HttpStatusCode.NotFound)
{
var db = client.CreateDatabaseAsync(new Microsoft.Azure.Documents.Database() { Id = "RNPbooking" }).Result;
return true;
}
return false;
});
}
try
{
var collection = client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri("RNPbooking", "AspNetIdentity")).Result;
}
catch (AggregateException ae)
{
ae.Handle(ex =>
{
if (ex.GetType() == typeof(DocumentClientException) && ((DocumentClientException)ex).StatusCode == HttpStatusCode.NotFound)
{
DocumentCollection collection = new DocumentCollection()
{
Id = "AspNetIdentity"
};
collection = client.CreateDocumentCollectionAsync(UriFactory.CreateDatabaseUri("RNPbooking"),collection).Result;
return true;
}
return false;
});
}
}
Controller
[Authorize(Roles = "Admin)]
public class AdminController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public CosmosClient _client;
public AdminController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
)
{
_userManager = userManager;
_signInManager = signInManager;
}
You need to fill in CreateDocumentCollectionUri method with FeedOptions object as a parameter
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),new FeedOptions { EnableCrossPartitionQuery=true})
UPDATED: From the code examples, you seem to be using this library https://github.com/codekoenig/AspNetCore.Identity.DocumentDb, AspNetCore.Identity.DocumentDb.
This error means the library you are using is performing a Document Query in their code at some point, it is not related to the creation of the Database or Collection.
The library code must be using CreateDocumentQuery somewhere, that code is missing:
new FeedOptions { EnableCrossPartitionQuery = true };
If you search their code base, you will see multiple scenarios like that: https://github.com/codekoenig/AspNetCore.Identity.DocumentDb/search?q=CreateDocumentQuery
Because this code is out of your control, you should try and contact the owner to see if this is a fix they can do on their end. The code for the library doesn't seem to have been updated in several years, so maybe this library is not maintained?
I am using version 3.3.2 of the ABP Framework. How can I add new methods to an existing controller? I want to extend the IdentityUserController. Following the docs I am creating my own implementation as following:
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IdentityUserController))]
public class MyIdentityUserController : IdentityUserController
{
public MyIdentityUserController(IIdentityUserAppService userAppService) : base(userAppService)
{
}
public override Task<PagedResultDto<IdentityUserDto>> GetListAsync(GetIdentityUsersInput input)
{
return base.GetListAsync(input);
}
[HttpGet]
[Route("my-method")]
public Task<string> MyMethod()
{
return Task.FromResult("Works");
}
}
The overrides actually work but my custom method is not visible in Swagger and when I try to access it with Postman it is not accessible either. Any ideas how I can extend existing controllers? I don't want to create a whole new controller since I have a combination with overrides and new methods. I would like to keep everything together.
First, set IncludeSelf = true — we will use this to determine whether to replace the existing controller with the extended controller, and ASP.NET Core will resolve your controller by class.
Optionally, add [ControllerName("User")] from IdentityUserController since it is not inherited:
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IdentityUserController), IncludeSelf = true)]
[ControllerName("User")]
public class MyIdentityUserController : IdentityUserController
Option 1
Subclass AbpServiceConvention and override RemoveDuplicateControllers to remove the existing controller(s) instead of your extended controller:
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr.IncludeSelf)
{
var existingControllerModels = application.Controllers
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType))
.ToArray();
derivedControllerModels.AddRange(existingControllerModels);
Logger.LogInformation($"Removing the controller{(existingControllerModels.Length > 1 ? "s" : "")} {exposeServicesAttr.ServiceTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(existingControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}");
continue;
}
Full code of subclass:
public class MyAbpServiceConvention : AbpServiceConvention
{
public MyAbpServiceConvention(
IOptions<AbpAspNetCoreMvcOptions> options,
IConventionalRouteBuilder conventionalRouteBuilder)
: base(options, conventionalRouteBuilder)
{
}
protected override void RemoveDuplicateControllers(ApplicationModel application)
{
var derivedControllerModels = new List<ControllerModel>();
foreach (var controllerModel in application.Controllers)
{
if (!controllerModel.ControllerType.IsDefined(typeof(ExposeServicesAttribute), false))
{
continue;
}
if (Options.IgnoredControllersOnModelExclusion.Contains(controllerModel.ControllerType))
{
continue;
}
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr.IncludeSelf)
{
var existingControllerModels = application.Controllers
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType))
.ToArray();
derivedControllerModels.AddRange(existingControllerModels);
Logger.LogInformation($"Removing the controller{(existingControllerModels.Length > 1 ? "s" : "")} {exposeServicesAttr.ServiceTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(existingControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}");
continue;
}
var baseControllerTypes = controllerModel.ControllerType
.GetBaseClasses(typeof(Controller), includeObject: false)
.Where(t => !t.IsAbstract)
.ToArray();
if (baseControllerTypes.Length > 0)
{
derivedControllerModels.Add(controllerModel);
Logger.LogInformation($"Removing the controller {controllerModel.ControllerType.AssemblyQualifiedName} from the application model since it replaces the controller(s): {baseControllerTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")}");
}
}
application.Controllers.RemoveAll(derivedControllerModels);
}
}
Option 2
Implement IApplicationModelConvention to add your extended controller to IgnoredControllersOnModelExclusion and remove the existing controller:
public class ExtendedControllerApplicationModelConvention : IApplicationModelConvention
{
private readonly Lazy<IOptions<AbpAspNetCoreMvcOptions>> _lazyOptions;
public ExtendedControllerApplicationModelConvention (IServiceCollection services)
{
_lazyOptions = services.GetRequiredServiceLazy<IOptions<AbpAspNetCoreMvcOptions>>();
}
public void Apply(ApplicationModel application)
{
var controllerModelsToRemove = new List<ControllerModel>();
var ignoredControllersOnModelExclusion = _lazyOptions.Value.Value.IgnoredControllersOnModelExclusion;
foreach (var controllerModel in application.Controllers)
{
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr != null && exposeServicesAttr.IncludeSelf)
{
ignoredControllersOnModelExclusion.AddIfNotContains(controllerModel.ControllerType);
var existingControllerModels = application.Controllers
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType));
controllerModelsToRemove.AddIfNotContains(existingControllerModels);
}
}
application.Controllers.RemoveAll(controllerModelsToRemove);
}
}
In your module, insert ExtendedServiceApplicationModelConvention before AbpServiceConventionWrapper:
public override void ConfigureServices(ServiceConfigurationContext context)
{
// ...
Configure<MvcOptions>(options =>
{
var abpServiceConvention = options.Conventions.OfType<AbpServiceConventionWrapper>().First();
options.Conventions.InsertBefore(abpServiceConvention, new ExtendedControllerApplicationModelConvention (context.Services));
});
}
I created a test project using the same version of ABP v3.3.2 and managed to get this working.
You can override the original methods in a new class that inherits from the original IdentityUserController, but you need to create your own controller to 'add' new methods to it. If you create a new controller that includes the same class attributes as IdentityUserController then it will appear like it has been extended.
[RemoteService(Name = IdentityRemoteServiceConsts.RemoteServiceName)]
[Area("identity")]
[ControllerName("User")]
[Route("api/identity/users")]
[ExposeServices(typeof(MyIdentityUserController))]
public class MyIdentityUserController : AbpController, IApplicationService, IRemoteService
{
[HttpGet("my-method")]
public Task<string> MyMethod()
{
return Task.FromResult("Works");
}
}
I have problem with the Unit Test my Module from Business Layer.
I'm using Moq.
my Module from DL :
namespace EF.BusinessLayer.Modules
{
public class UserModule : IUserModule
{
public User AddUser(User user)
{
using (IUnitOfWork uow = IoC.Resolve<IUnitOfWork>())
{
uow.Add(user);
uow.SaveChanges();
return uow.Queryable<User>().Where(x => x.Username == user.Username).FirstOrDefault();
}
}
}
}
I'm trying to write a test, with which I can test if my entity was added properly.
[TestMethod]
public void AddUserTestMethod()
{
User user = new User()
{
FirstName = "Criss",
LastName = "Johnson",
Username = "CJ",
Email = "email#cj.com"
};
var mockContext = new Mock<IUserModule>();
mockContext.Setup<User>(x => x.AddUser(user)).Callback<User>((c) => User = c);
var result = mockContext.Object.AddUser(user);
}
but results = null.
What I'm doing wrong ?
Lets understand what is your system under test (SUT). From your description, I think it's UserModule.
Note that UserModule depends on IUnitOfWork, which you have very rightly dependency injected using an IoC container.
One of the key things of unit testing is to test the SUT in isolation. Which means for testing the business logic under UserModule, you should be mocking the IUnitOfWork. That's the problem I see with your test. It mocks the UserModule itself.
I suggest you to register the mocked IUnitOfWork in your IoC container before calling the "AddUser" method of UserModule.
[TestMethod]
public void AddUserTestMethod()
{
/*given*/
User expectedUser = new User()
{
FirstName = "Criss",
LastName = "Johnson",
Username = "CJ",
Email = "email#cj.com"
};
var users = new List<User>();
users.Add(expectedUser);
//mock IUnitWork
var mockUnitOfWork= new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(x => x.Add(user));
mockUnitOfWork.Setup(x => x.SaveChanges());
//This may not work as is, could need modification
mockUnitOfWork.Setup(x => x.Queryable<User>()).Returns(users);
//Register mocked unit of work in IoC container
IoC.Register<IUnitOfWork>(mockUnitOfWork.Object);
//Instantiate SUT
var userModule = new UserModule();
/*when*/
var result = userModule.AddUser(user);
/*then*/
//Assert here
}
I'm having some serious issues with Fluent Nhibernate in my ASP.NET WebForms app when trying to modify a child object and then saving the parent object.
My solution is currently made of 2 projects :
Core : A class library where all entities & repositories classes are located
Website : The ASP.NET 4.5 WebForms application
Here is my simple mapping for my Employee object:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateCreated);
Map(x => x.Username);
Map(x => x.FirstName);
Map(x => x.LastName);
HasMany(x => x.TimeEntries).Inverse().Cascade.All().KeyColumn("Employee_id");
}
}
Here is my my mapping for the TimeEntry object:
public class TimeEntryMap : ClassMap<TimeEntry>
{
public TimeEntryMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateCreated);
Map(x => x.Date);
Map(x => x.Length);
References(x => x.Employee).Column("Employee_id").Not.Nullable();
}
}
As stated in the title, i'm using one session per request in my web app, using this code in Gobal.asax:
public static ISessionFactory SessionFactory = Core.SessionFactoryManager.CreateSessionFactory();
public static ISession CurrentSession
{
get { return (ISession)HttpContext.Current.Items["current.session"]; }
set { HttpContext.Current.Items["current.session"] = value; }
}
protected Global()
{
BeginRequest += delegate
{
System.Diagnostics.Debug.WriteLine("New Session");
CurrentSession = SessionFactory.OpenSession();
};
EndRequest += delegate
{
if (CurrentSession != null)
CurrentSession.Dispose();
};
}
Also, here is my SessionFactoryManager class:
public class SessionFactoryManager
{
public static ISession CurrentSession;
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("Website.Properties.Settings.WebSiteConnString")))
.Mappings(m => m
.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true))
.BuildSessionFactory();
}
public static ISession GetSession()
{
return (ISession)HttpContext.Current.Items["current.session"];
}
}
Here is one of my repository class, the one i use to handle the Employee's object data operations:
public class EmployeeRepository<T> : IRepository<T> where T : Employee
{
private readonly ISession _session;
public EmployeeRepository(ISession session)
{
_session = session;
}
public T GetById(int id)
{
T result = null;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Get<T>(id);
tx.Commit();
}
return result;
}
public IList<T> GetAll()
{
IList<T> result = null;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Query<T>().ToList();
tx.Commit();
}
return result;
}
public bool Save(T item)
{
var result = false;
using (ITransaction tx = _session.BeginTransaction())
{
_session.SaveOrUpdate(item);
tx.Commit();
result = true;
}
return result;
}
public bool Delete(T item)
{
var result = false;
using (ITransaction tx = _session.BeginTransaction())
{
_session.Delete(_session.Load(typeof (T), item.Id));
tx.Commit();
result = true;
}
return result;
}
public int Count()
{
var result = 0;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Query<T>().Count();
tx.Commit();
}
return result;
}
}
Now, here is my problem. When i'm trying to insert Employee(s), everything is fine. Updating is also perfect... well, as long as i'm not updating one of the TimeEntry object referenced in the "TimeEntries" property of Employee...
Here is where an exception is raised (in a ASPX file of the web project):
var emp = new Employee(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
emp.Save();
Here is the exception that is raised:
[NonUniqueObjectException: a different object with the same identifier
value was already associated with the session: 1, of entity:
Core.Entities.Employee]
Basically, whenever I try to
Load an employee and
Modify one of the saved TimeEntry, I get that exception.
FYI, I tried replacing the SaveOrUpdate() in the repository for Merge(). It did an excellent job, but when creating an object using Merge(), my object never gets it's Id set.
I also tried creating and flushing the ISession in each function of my repository. It made no sense because as soon as i was trying to load the TimeEntries property of an Employee, an exception was raised, saying the object could not be lazy-loaded as the ISession was closed...
I'm at lost and would appreciate some help. Any suggestion for my repository is also welcome, as i'm quite new to this.
Thanks you guys!
This code
var emp = new Employee(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
emp.Save();
is creating a new Employee object, presumable with an ID of 1 passed through the constructor. You should be loading the Employee from the database, and your Employee object should not allow the ID to be set since you are using an identity column. Also, a new Employee would not have any TimeEntries and the error message clearly points to an Employee instance as the problem.
I'm not a fan of transactions inside repositories and I'm really not a fan of generic repositories. Why is your EmployeeRepository a generic? Shouldn't it be
public class EmployeeRepository : IRepository<Employee>
I think your code should look something like:
var repository = new EmployeeRepository(session);
var emp = repository.GetById(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
repository.Save(emp);
Personally I prefer to work directly with the ISession:
using (var txn = _session.BeginTransaction())
{
var emp = _session.Get<Employee>(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
txn.Commit();
}
This StackOverflow Answer gives an excellent description of using merge.
But...
I believe that you are facing issues with setting up a correct session pattern for your application.
I you suggest to take a look at session-per-request pattern
where in you create a single NHibernate session object per request. the session is opened when the request is received and closed/flushed on generating a response.
Also make sure that instead of using SessionFactory.OpenSession() to get a session try using SessionFactory.GetCurrentSession() which puts the onus on NHibernate to return you the current correct session.
I hope this pushes you in the right direction.
I'm trying to write this simple test:
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var postProcessingAction = fixture.Freeze<Mock<IPostProcessingAction>>();
var postProcessor = fixture.Freeze<PostProcessor>();
postProcessor.Process("", "");
postProcessingAction.Verify(action => action.Do());
The Verify check fails.
The code for postProcessor.Process is
public void Process(string resultFilePath, string jobId)
{
IPostProcessingAction postProcessingAction =
postProcessingActionReader
.CreatePostProcessingActionFromJobResultXml(resultFilePath);
postProcessingAction.Do();
}
postProcessingActionReader is an interface field initialized through the constructor.
I'm expecting the test to pass but it fails, it turns out the instance of IPostProessingAction returned from the CreatePostProcessingActionFromJobResultXml method is not the same instance as returned from fixture.Freeze<>.
My expectation was that after freezing this Mock object it would inject the underlying mock of the IPostProcessingAction interface in every place its required as well as make all mock methods returning IPostProcessingAction return this same object.
Is my expectation about the return value of the mock methods incorrect?
Is there a way to change this behavior so that mock methods return the same frozen instance?
You need to Freeze the IPostProcessingActionReader component.
The following test will pass:
[Fact]
public void Test()
{
var fixture = new Fixture()
.Customize(new AutoMoqCustomization());
var postProcessingActionMock = new Mock<IPostProcessingAction>();
var postProcessingActionReaderMock = fixture
.Freeze<Mock<IPostProcessingActionReader>>();
postProcessingActionReaderMock
.Setup(x => x.CreatePostProcessingActionFromJobResultXml(
It.IsAny<string>()))
.Returns(postProcessingActionMock.Object);
var postProcessor = fixture.CreateAnonymous<PostProcessor>();
postProcessor.Process("", "");
postProcessingActionMock.Verify(action => action.Do());
}
Assuming that the types are defined as:
public interface IPostProcessingAction
{
void Do();
}
public class PostProcessor
{
private readonly IPostProcessingActionReader actionReader;
public PostProcessor(IPostProcessingActionReader actionReader)
{
if (actionReader == null)
throw new ArgumentNullException("actionReader");
this.actionReader = actionReader;
}
public void Process(string resultFilePath, string jobId)
{
IPostProcessingAction postProcessingAction = this.actionReader
.CreatePostProcessingActionFromJobResultXml(resultFilePath);
postProcessingAction.Do();
}
}
public interface IPostProcessingActionReader
{
IPostProcessingAction CreatePostProcessingActionFromJobResultXml(
string resultFilePath);
}
In case you use AutoFixture declaratively with the xUnit.net extension the test could be simplified even further:
[Theory, AutoMoqData]
public void Test(
[Frozen]Mock<IPostProcessingActionReader> readerMock,
Mock<IPostProcessingAction> postProcessingActionMock,
PostProcessor postProcessor)
{
readerMock
.Setup(x => x.CreatePostProcessingActionFromJobResultXml(
It.IsAny<string>()))
.Returns(postProcessingActionMock.Object);
postProcessor.Process("", "");
postProcessingActionMock.Verify(action => action.Do());
}
The AutoMoqDataAttribute is defined as:
internal class AutoMoqDataAttribute : AutoDataAttribute
{
internal AutoMoqDataAttribute()
: base(new Fixture().Customize(new AutoMoqCustomization()))
{
}
}
As of 3.20.0, you can use AutoConfiguredMoqCustomization. This will automatically configure all mocks so that their members' return values are generated by AutoFixture.
In other words, it will auto-configure your postProcessingActionReader to return the frozen postProcessingAction.
Just change this:
var fixture = new Fixture().Customize(new AutoMoqCustomization());
to this:
var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());