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...)
Related
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.
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.
I am unit-testing an async method that returns a List<T>. This method has a dependency on a mapping class/interface. In my unit-test, I am mocking the mapping class using moq. The test runs okay, and the returned list has items, but the values of the items is null. I think the problem is because I haven't stubbed-out the mapping classes methods properly. I don't have a lot of experience with testing, so any guidance is appreciated.
Test Method:
[TestMethod]
[TestCategory("CSR.Data.Tests.Services.ServiceSearchTest")]
public void SearchAccount()
{
// Arrange
var mapper = new Mock<CSR.Data.Mapping.Interfaces.IMapper<Account, AccountDTO>>();
mapper.Setup(i => i.Initialize());
mapper.Setup(i => i.ToDomain(It.IsAny<AccountSearchResult>())).Returns(It.IsAny<Account>);
mapper.Setup(i => i.DomainToDto(It.IsAny<Account>())).Returns(It.IsAny<AccountDTO>);
var service = new ServiceSearch(null,mapper.Object);
string accountNumber = "123";
string accountName = "";
// Act
var results = service.SearchAccount(accountNumber, accountName);
// Assert
Assert.IsTrue(results.Result.Count >= 1);
}
Method/Class That I'm Testing:
public class ServiceSearch : IServiceSearch
{
public ServiceSearch(IMapper<Claim, ClaimDTO> claimMapper, IMapper<Account, AccountDTO> accountMapper)
{
_claimMapper = claimMapper;
_accountMapper = accountMapper;
}
public async Task<List<AccountDTO>> SearchAccount(string accountNumber, string accountName)
{
var accounts = new List<Account>();
var accountDTOs = new List<AccountDTO>();
var results = await Task.Run(() => base.AccountSearch(accountNumber, accountName).Result);
if (results != null && results.Count > 0)
{
//Map DH to Domain
_accountMapper.Initialize();
foreach (AccountSearchResult result in results)
{
accounts.Add(_accountMapper.ToDomain(result));
}
//Map Domain to DTO
foreach (Account account in accounts)
{
accountDTOs.Add(_accountMapper.DomainToDto(account));
}
}
return accountDTOs;
}
}
This isn't the best place to use a Mock object because you are going to spend a lot of time writing your test objects and mock results. The issue with the setup call is that you haven't configured anything to send back in the result. A correct example would be:
// you would fully configure this object
AccountDTO expectedResult = new AccountDTO();
mapper.Setup(i => i.ToDomain(It.IsAny<AccountSearchResult>())).Returns(expectedResult);
Now you can use the setup to configure different accountDTOs for different inputs.
You call also configure a callback to generate the account at test time:
mapper.Setup(i => i.ToDomain(It.IsAny<AccountSearchResult>())).Returns<AccountSearchResult>(sr => {
// build and return your dto here
});
However, unless your mapper is expensive to run or create, I think you'd better off just ensure that it is fully tested and acceptable and then use it to go ahead and generate the DTOs directly instead of trying to mock it out.
You don't actually setup an object in the ".Returns" call. You need to make sure to setup the ".Returns" to actually have an object with values.
I am developing Moq tests for various entities. I can setup create and delete tests fine, but not update - the entity in the repository does not change. I know this is due to the PersistAll doing nothing (probably due to a setup I am missing).
This is a sample of an insert persist setup (I am looking for an Update version):
agg.Setup(a => a.InsertOnPersist<Thingy>(model)).Callback(() => mockThingies.Add(model));
In addition, I also have this to link the List to being the repository:
agg.Setup(a => a.GetObjectStore<Artist>()).Returns(mockThingies.AsQueryable());
This is a sample of an update test I have:
public List<Thingy> mockThingies; //this is our repository
[TestInitialize]
public void SetupTests()
{
mockThingies= new List<Thingy>();
Thingy someThingy = new Thingy();
someThingy.Name = "MyName";
someThingy.ID = 1;
mockThingies.Add(someThingy);
}
[TestMethod]
public void CanEditExistingThingy()
{
Mock<BusinessExceptionBroadcaster> beb = new Mock<BusinessExceptionBroadcaster>();
Mock<IValidationEngine> valid = new Mock<IValidationEngine>();
Mock<IAggregate> agg = new Mock<IAggregate>();
agg.Setup(a => a.GetObjectStore<Thingy>()).Returns(mockThingies.AsQueryable());
ThingyRepository repo = new ThingyRepository (agg.Object);
ThingyService service = new ThingyService (repo, beb.Object, valid.Object);
Thingy newThingy = new Thingy();
newThingy.ID = 1; //same as old
newThingy.Name = "newname"; //new name
Assert.AreNotEqual(newThingy.Name,mockThingies[0].Name);
Assert.IsTrue(service.Update(newThingy));
Assert.AreEqual(newThingy.Name, mockThingies[0].Name); //FAILS HERE
}
This is the code to update:
public bool Update(Thingy entity)
{
Thingy existingThingy= _Thingy.FirstOrDefault(t=>t.ID == entity.ID);
if (existingThingy != null)
{
_Thingy.PersistAll();
return true;
}
else
{
//unimportant
}
}
return false;
}
Don't worry about testing whether the update call actually updates something. You'll just want to verify that your service calls the appropriate method on the repo to perform the update and persist. Testing the actual update is a little outside the scope of this one test.
As far as I can see, it can't work, because you're setting one Thingy with ID=1 in Setup, and then create other one with same ID in test. Although they share same ID, they are not same, so your changes can't be ever propagated to repository.
In fact, I think that it's a bug in your CUT code, because while you're testing ID match, you don't test that your repository knows something about entity you're updating. To add, I personally think that there's something wrong with your repository design if it allows such things.
If we were talking about EntityFramework I'd say you have to attach your entity to context.
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.