Mocking an NHibernate ISession with Moq - c#

I am starting a new project with NHibernate, ASP.NET MVC 2.0 and StructureMap and using NUnit and Moq for testing. For each of my controllers I have a single public constructor into which an ISession is being injected. The application itself works just fine, but in terms of unit testing I essentially have to mock an ISession in order to test the controllers.
When I attempt to Mock the ISession with MOQ i get the following error message:
Only property accesses are supported
in intermediate invocations
It appears that my problem is expecting List of users from the framework CreateQuery method but after googling the issue I am now clearer.
I have two questions:
1) Is this the WRONG way to mock dependency injection of an ISession
2) Is there a way to modify the code so that it can successfully return my list
[Test]
public void DummyTest()
{
var mock = new Mock<ISession>();
var loc = new Mock<User>();
loc.SetupGet(x => x.ID).Returns(2);
loc.SetupGet(x => x.FirstName).Returns("John");
loc.SetupGet(x => x.LastName).Returns("Peterson");
var lst = new List<User> {loc.Object};
mock.Setup(framework => framework.CreateQuery("from User").List<User>()).Returns(lst);
var controller = new UsersController(mock.Object);
var result = controller.Index() as ViewResult;
Assert.IsNotNull(result.ViewData);
}
Please note, I am pretty sure I could just create a hard-coded list of users (rather than mocking an individual User and adding it to a list) but figured I'd leave the code as I have it right now.
Also, the Index action of this particular controller essentially executes the CreateQuery call mimicked above to return all users in the database. This is a contrived example - don't read anything into the details.
Thanks in advance for your help
Edit: In reply to the below comment, I am adding the stacktrace for the error. Also, all properties on the User class are virtual.
TestCase
'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView'
failed: System.NotSupportedException :
Only property accesses are supported
in intermediate invocations on a
setup. Unsupported expression
framework.CreateQuery("from User").
at
Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression
m) at
Moq.ExpressionVisitor.Visit(Expression
exp) at
Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression
m) at
Moq.ExpressionVisitor.Visit(Expression
exp) at
Moq.Mock.AutoMockPropertiesVisitor.SetupMocks(Expression
expression) at
Moq.Mock.GetInterceptor(LambdaExpression
lambda, Mock mock) at
Moq.Mock.<>c__DisplayClass122.<Setup>b__11()
at Moq.PexProtector.Invoke[T](Func1
function) at
Moq.Mock.Setup[T1,TResult](Mock mock,
Expression1 expression) at
Moq.Mock1.Setup[TResult](Expression`1
expression)
Controllers\UserControllerTest.cs(29,0):
at
Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

Below is the solution I came up with which seems to work perfectly. Again, I am not testing NHibernate and I am not testing the database - I simply want to test the controllers which depend on NHibernate. The issue with the initial solution appears to be the fact that I was calling a Method as well as reading the List member of the session in the MOQ setup call. I broke up these calls by breaking the solution into a QueryMock and a Session Mock (create query returns an IQuery object). A transaction mock was also necessary as it is a dependency (in my case) of the session...
[Test]
public void DummyTest()
{
var userList = new List<User>() { new User() { ID = 2, FirstName = "John", LastName = "Peterson" } };
var sessionMock = new Mock<ISession>();
var queryMock = new Mock<IQuery>();
var transactionMock = new Mock<ITransaction>();
sessionMock.SetupGet(x => x.Transaction).Returns(transactionMock.Object);
sessionMock.Setup(session => session.CreateQuery("from User")).Returns(queryMock.Object);
queryMock.Setup(x => x.List<User>()).Returns(userList);
var controller = new UsersController(sessionMock.Object);
var result = controller.Index() as ViewResult;
Assert.IsNotNull(result.ViewData);
}

Rather than mocking the Session, one might consider setting up a different Configuration for unit-tests. This unit-testing Configuration uses a fast, in-process database like SQLite or Firebird. In the fixture setup, you create a new test database completely from scratch, run the scripts to set up the tables, and create a set of initial records. In the per-test setup, you open a transaction and in the post-test teardown, you rollback the transaction to restore the database to its previous state. In a sense, you are not mocking the Session, because that gets tricky, but you are mocking the actual database.

Related

Mocking Entity Framework Core context

I try to test my app so I need to mock my EF context.
My code seems to be ok, but I have following exception:
"System.ArgumentNullException : Value cannot be null. Parameter name:
source"
Here is my test method:
var options = new DbContextOptionsBuilder<ProductContext>().Options;
var settings = new SqlSettings
{
InMemory = true
};
var context = new Mock<ProductContext>(options, settings);
var mockTreeService = new TreeService(context.Object);
await mockTreeService.CreateTreeAsync("Testing tree", Guid.NewGuid());
context.Verify(x => x.AddAsync(It.IsAny<Tree>(), CancellationToken.None), Times.Once);
It looks like that this exception is thrown during executing this piece of code
var tree = await _context.Trees
.Include(x => x.Translation)
.FirstOrDefaultAsync(x => x.Translation.Pl == name);
It comes from my service which I'm testing
I think this is due to not having a connection string set. Frankly, it's a bit difficult to fully mock out DbContext, which is why the EF Core team has provided an in-memory implementation. This is far easier to work with for testing purposes. Just change your options initialization to:
var options = new DbContextOptionsBuilder<ProductContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
Afterwards, you'll need to populate the database with your test data. Then, you can run the rest of your test.
Note: if you're using the in-memory database, you don't need to mock the context anymore, so you can remove that bit of code. The in-memory database is essentially, itself, a mock.
I have used this https://github.com/huysentruitw/entity-framework-core-mock library. Very easy and can write unit test using less coding.
You can use most of Moq methods if you are using moq framework.
Below is example code for test DBQuerys.
public async Task<Boat> GetByIdAsync(string id)
=> await _boatContext.Boats.Where(x => x.id == id).FirstOrDefaultAsync();
[Fact]
public async Task GetByIdAsync_WhenCalled_ReturnsItem()
{
// Arrange
var models = new[] { new Boat { id = "p1" } };
var dbContextMock = new DbContextMock<BoatContext>();
dbContextMock.CreateDbQueryMock(x => x.Boats, models);
var service = new Properties(dbContextMock.Object);
// Act
var okResult = await service.GetByIdAsync("p1");
// Assert
Assert.IsType<Boat>(okResult.Result);
}
Posting here this may help someone :)
Try to use my Moq/NSubstitute extension MockQueryable: https://github.com/romantitov/MockQueryable
supported all Sync/Async operations
//1 - create a List<T> with test items
var users = new List<UserEntity>()
{
new UserEntity,
...
};
//2 - build mock by extension
var mock = users.AsQueryable().BuildMock();
//3 - setup the mock as Queryable for Moq
_userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);
//3 - setup the mock as Queryable for NSubstitute
_userRepository.GetQueryable().Returns(mock);
DbSet also supported
//2 - build mock by extension
var mock = users.AsQueryable().BuildMockDbSet();
//3 - setup DbSet for Moq
var userRepository = new TestDbSetRepository(mock.Object);
//3 - setup DbSet for NSubstitute
var userRepository = new TestDbSetRepository(mock);
Note:
AutoMapper supported from 1.0.4 ver
DbQuery supported from 1.1.0 ver
I don't think it's correct to Mock the DbContext. You should be mocking your repositories in your testing... mocking the DbContext is you basically testing Microsoft's code... which is dumb because they already do that. So again... all of your data access should go through repositories (see Repository Pattern) and you should be mocking those in your testing, not the DbContext.

.NET Core how to unit test service?

I have build a WebAPI and want to create a unit test project to have my services tested automatically.
The flow of my WebAPI is simple:
Controller (DI Service) -> Service (DI Repository) -> _repo CRUD
Suppose I have a service like:
public int Cancel(string id) //change status filed to 'n'
{
var item = _repo.Find(id);
item.status = "n";
_repo.Update(item);
return _repo.SaveChanges();
}
And I want to build a unit test, which just use InMemoryDatabase.
public void Cancel_StatusShouldBeN() //Testing Cancel() method of a service
{
_service.Insert(item);
int rs = _service.Cancel(item.Id);
Assert.Equal(1, rs);
item = _service.GetByid(item.Id);
Assert.Equal("n", item.status);
}
I've searched other related question, found that
You can't use dependency injections on test classes.
I just want to know if there is any other solution to achive my unit test idea?
When unit testing, you should just supply all the dependencies of the class you are testing explicitly. That is dependency injection; not having the service construct its dependencies on its own but making it rely on the outer component to provide them. When you are outside of a dependency injection container and inside a unit test where you are manually creating the class you are testing, it’s your responsibility to provide the dependencies.
In practice, this means that you either provide mocks or actual objects to the constructor. For example, you might want to provide a real logger but without a target, a real database context with a connected in-memory database, or some mocked service.
Let’s assume for this example, that the service you are testing looks like this:
public class ExampleService
{
public ExampleService(ILogger<ExampleService> logger,
MyDbContext databaseContext,
UtilityService utilityService)
{
// …
}
// …
}
So in order to test ExampleService, we need to provide those three objects. In this case, we will do the following for each:
ILogger<ExampleService> – we will use a real logger, without any attached target. So any call on the logger will work properly without us having to provide some mock, but we do not need to test the log output, so we do not need a real target
MyDbContext – Here, we’ll use the real database context with an attached in-memory database
UtilityService – For this, we will create a mock which just setups the utility method we need inside the methods we want to test.
So a unit test could look like this:
[Fact]
public async Task TestExampleMethod()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// using Moq as the mocking library
var utilityServiceMock = new Mock<UtilityService>();
utilityServiceMock.Setup(u => u.GetRandomNumber()).Returns(4);
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<Customer>().Add(new Customer()
{
Id = 2,
Name = "Foo bar"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new ExampleService(logger, db, utilityServiceMock.Object);
// act
var result = service.DoSomethingWithCustomer(2);
// assert
Assert.NotNull(result);
Assert.Equal(2, result.CustomerId);
Assert.Equal("Foo bar", result.CustomerName);
Assert.Equal(4, result.SomeRandomNumber);
}
}
In your specific Cancel case, you want to avoid using any methods of the service you are not currently testing. So if you want to test Cancel, the only method you should call from your service is Cancel. A test could look like this (just guessing the dependencies here):
[Fact]
public async Task Cancel_StatusShouldBeN()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<SomeItem>().Add(new SomeItem()
{
Id = 5,
Status = "Not N"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new YourService(logger, db);
// act
var result = service.Cancel(5);
// assert
Assert.Equal(1, result);
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
var item = db.Set<SomeItem>().Find(5);
Assert.Equal(5, item.Id);
Assert.Equal("n", item.Status);
}
}
Btw. note that I’m opening up a new database context all the time in order to avoid getting results from the cached entities. By opening a new context, I can verify that the changes actually made it into the database completely.

Unit Testing Codeactivity Workflow

I am trying to write a unit test for a CodeActivity Workflow. My test so far is only
[TestMethod]
public void BusinessUnitTest()
{
WorkflowInvoker invoker = new WorkflowInvoker(new DespatchStockOrder());
invoker.Extensions.Add<ITracingService>(() => tracingService);
invoker.Extensions.Add<IWorkflowContext>(() => workflowContext);
invoker.Extensions.Add<IOrganizationServiceFactory>(() => factory);
IDictionary<string, object> outputs = invoker.Invoke();
}
This works fine and runs the workflow. However I get a null referrence when I hit this line in the workflow.
// Create the context
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
if (context == null)
{
throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
}
Entity entity = new Entity(context.PrimaryEntityName) //This line errors with PrimaryEntityName null
{
Id = context.PrimaryEntityId,
LogicalName = context.PrimaryEntityName
};
My question is how do I get context.PrimaryEntityName witha value in a unit test
If you are building the context manually then you will need to populate the PrimaryEntityName in the [TestMethod]. You should probably look further into how your workflowContext test object is constructed.
[TestMethod]
public void BusinessUnitTest()
{
workflowContext.PrimaryEntityName = "Entity name";
WorkflowInvoker invoker = new WorkflowInvoker(new DespatchStockOrder());
invoker.Extensions.Add<ITracingService>(() => tracingService);
invoker.Extensions.Add<IWorkflowContext>(() => workflowContext);
invoker.Extensions.Add<IOrganizationServiceFactory>(() => factory);
IDictionary<string, object> outputs = invoker.Invoke();
}
You might want to try FakeXrmEasy. It's an open source unit testing library I'm maintaining to facilitate unit testing for Dynamics Crm without mocking anything.
You get a reference to a workflow context and inject whatever parameters you need, including Primary Entity names and Id's.
But the most common crm messages are already mocked (retrieve multiple requests, CRUD, fetchxml, LINQ, QueryExpressions, and so no...)

How to mock method with FilterExpression input parameter?

I’m trying to mock method using Moq that takes FilterExpression as an input parameter and I have no luck so far. When I run it is always returns null. If I pass NULL instead of FilterExpression it works.
Here is my code:
Repository
public interface ITestRepository
{
string Test(int id, FilterExpression fe);
}
public class TestRepository : ITestRepository
{
public stringTest(int id, FilterExpression fe)
{
throw new NotImplementedException();
}
}
Unit Test
var testMock = new Mock<ITestRepository>();
var fe = new FilterExpression();
var result = “Hello World”;
var id = 1;
//DOESN’T WORK
testMock.Setup(r => r.Test(id, filterExpression)).Returns(result);
//WORKS
testMock.Setup(r => r.Test(id, null)).Returns(result);
//Test
[TestMethod]
public void test()
{
var fe = new FilterExpression();
var id = 1;
_testRepository.Test(id, fe);
_testRepository.Test(id, null);
}
Arguments don't match. You use different FilterExpressions. Have you tried to put any FilterExpression?
testMock.Setup(r => r.Test(id, It.IsAny<FilterExpression>())).Returns(result);
Your setup should contain the original object; in your code sample it does not:
testMock.Setup(r => r.Test(id, filterExpression)).Returns(result);
to
testMock.Setup(r => r.Test(id, fe)).Returns(result);
According to your test above. Also, where is your setup of the test objects defined? these should also be defined within the test itself.
If you are trying to unit test Dynamics CRM code, then did you try FakeXrmEasy?
It's an open source project I have been working on since 2014 which automatically mocks IOrganizationService calls for you, including your FilterExpressions, but also any QueryExpression (with joins, filters, nested linked entities, order by), QueryByAttribute, FetchXml, the main basic CRUD and also other messages like Associate, Disassociate, ExecuteMultiple, WhoAmIRequests, and so on and so on...
It's really robust, it has more 800+ unit tests to test the testing framework itself (yeah, how meta is that? ) and the feedback, like this one, is excellent! :)
It definetely helps me on a daily basis and though of open sourcing it to help other CRM developers, and also so that other developers from the community could also contribute with their own improvements and new features (please do so).
It is available as a NuGet package too which makes it really easy to get started with. Please have a look at the documentation page here.
I actually started working on another repository to do the same thing for Web API calls from Javascript.
Stay tunned & happy testing!

Moq Test error Null on Service Layer

How can the following test be corrected to work?
[TestMethod()]
public void GetEmployeeProductivityTest()
{
var mockHR = new Mock<IRepository<Employee>>();
var mockCMS = new Mock<ICMS_Repository>();
mockCMS.Setup(repos => repos.FindEmployeeByUsername(It.IsAny<string>())).Verifiable();
Employee newEmp = new Employee();
newEmp.User_Name = "testName";
var service = new EmployeeService(mockHR.Object,mockCMS.Object);
var createResult = service.GetEmployeeByUserName(newEmp);
Assert.AreEqual(newEmp, createResult);
mockCMS.VerifyAll();
}
I get the following:
Assert.AreEqual failed. Expected:<Employee>. Actual:<(null)>.
As Requested this is the GetEmployeeByUserName() function being called:
public Employee GetEmployeeByUserName(Employee employee)
{
return _employeeRespository.Find().ByUserName(employee); <------(Using Strict: Gives me the following: All invocations on the mock must have a corresponding setup.)
}
Since you edited your question to show the behaviour of the GetEmployeeByUserName, it's now easy to explain why your test was failing.
mockCMS.Setup(repos => repos.FindEmployeeByUsername(It.IsAny<string>()))
Here you set up an expectation that the FindEmployeeByUsername(string) overload would be called, but then you go on to use the FindEmployeeByUsername(Employee) overload. Moq setups are for specific overloads so when the method is called the mocked service finds no matching setup. If there is no matching setup, the mock either returns the default value for the Employee type (null), or throws an exception, depending on which MockBehavior you chose.
In your updated test, you fixed this by setting up the overload that you actually use.
mockCMS.Setup(repos => repos.FindEmployeeByUsername(It.IsAny<Employee>()))
With simple methods like your GetEmployeeByUserName, mocking the dependencies and unit testing it can seem like a lot of overhead. What your test says is basically,
"when someone calls the GetEmployeeByUserName method on the EmployeeService,
the service should call the correct method on its repository"
Is this an important thing to assert? That's up to you to decide. As the complexity of your service methods increases, however, being able to isolate the dependencies and test their interactions will become more and more valuable.
You need to seed the FindEmployeeByUsername() Method of your mock:
mockCMS.Setup(repos => repos.FindEmployeeByUsername(It.IsAny<string>())).Returns(newEmp);
otherwise it wont return the expected object, while called within the EmployeeService.
Instead of using multiple repositories which I think was confusing. I simplified it and it works now! Except I'm still not sure how this helps me code better. What did I accomplish with this? (I'm new to Testing/Moq/Integration Tests...etc..) I would really like an answer...to this..
public void GetEmployeeUsername_Using_EmployeeClass()
{
var mockCMS = new Mock<ICMS_Repository>(MockBehavior.Strict);
Employee newEmp = new Employee();
newEmp.User_Name = "testName";
mockCMS.Setup(repos => repos.FindEmployeeByUsername(It.IsAny<Employee>())).Returns(newEmp);
var service = new EmployeeService(mockCMS.Object);
var createResult = service.GetEmployeeByUserName(newEmp);
Assert.AreEqual(newEmp, createResult);
}

Categories