I am trying to write down unit test for my repository which is interacting with MongoDb but facing a problem/exception in mocking the objects. This is what I have tried so far;
This is how my class looks like i.e. I have create a repository class and extend it from interface;
public class UserManagementRepository : IUserManagementRepository
{
private readonly IMongoCollection<UserModel> _users;
public UserManagementRepository(IDatabaseSettings dbSettings, IApplicationSettings applicationSettings, IMongoClient mongoClient)
{
IMongoDatabase database = mongoClient.GetDatabase(dbSettings.DatabaseName);
_users = database.GetCollection<UserModel>(applicationSettings.UserCollectionName);
}
public async Task<GeneralResponse> Get(string id)
{
GeneralResponse response = new GeneralResponse();
try
{
IAsyncCursor<UserModel> userModel = await _users.FindAsync(user => user.Id == id);
if (userModel != null)
{
response.Message = "User exists!";
response.Data = userModel.FirstOrDefault();
response.ResponseCode = ResponseCode.Success;
}
else
{
response.Message = $"User with Id: {id} not found!";
response.ResponseCode = ResponseCode.Success;
}
}
catch (Exception ex)
{
response.Message = "Failure";
response.ResponseCode = ResponseCode.Error;
}
return response;
}
}
This is how my Test class look like
public class UserManagmentRepositoryTests
{
private Mock<IDatabaseSettings> _mockDbSettings;
private Mock<IApplicationSettings> _mockApplicationSettings;
private Mock<IMongoClient> _mockClient;
public UserManagmentRepositoryTests()
{
_mockDbSettings = new Mock<IDatabaseSettings>();
_mockApplicationSettings = new Mock<IApplicationSettings>();
_mockClient = new Mock<IMongoClient>();
}
[Fact]
public async Task GetUserWithId_Test()
{
// Arrange
var repo = new Mock<IUserManagementRepository>();
IDatabaseSettings dbSettings = new DatabaseSettings()
{
ConnectionString = "mongodb:connectionstring",
DatabaseName = "dbname"
};
_mockDbSettings.Setup(x => x).Returns(dbSettings);
IApplicationSettings applicationSettings = new ApplicationSettings() { UserCollectionName = "users" };
_mockApplicationSettings.Setup(app => applicationSettings).Returns(applicationSettings);
MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(dbSettings.ConnectionString);
MongoClient client = new MongoClient(clientSettings);
_mockClient.Setup(c => client);
var ctr = new UserManagementRepository(_mockDbSettings.Object, _mockApplicationSettings.Object, _mockClient.Object);
// Act
var result = ctr.Get("132");
// Assert
//result.StatusCode.Should().NotBeNull();
}
}
I get an exception on every setup
You are trying to use ApplicationSettings as an expression. The setup method expects you to provide an expression for when to activate the mocked override.
In your case just get rid of the Mock<IApplicationSettings> altogether and mock all the objects you need to call any methods on:
[Fact]
public async Task GetUserWithId_Test()
{
// Arrange
var userModel = new UserModel() { Id = "123" };
var mockCursor = new Mock<IAsyncCursor<UserModel>>();
mockCursor
.Setup(c => c.FirstOrDefault())
.Returns(userModel);
var mockCollection = new Mock<IMongoCollection<UserModel>>();
Predicate<UserModel> filter = u => u.Id == userModel.Id;
mockCollection
.Setup(c => c.FindAsync(filter))
.ReturnsAsync(mockCursor.Object);
var mockDb = new Mock<IMongoDatabase>();
mockDb
.Setup(db => db.GetCollection<UserModel>(It.IsAny<string>()))
.Returns(mockCollection.Object);
_mockClient
.Setup(c => c.GetDatabase(It.IsAny<string>()))
.Returns(mockDb.Object)
var ctr = new UserManagementRepository(new(), new(), _mockClient.Object);
// Act
var result = ctr.Get("132");
// Assert
result.StatusCode.Should().Be(ResponseCode.Success);
result.Message.Should().Be("User exists!");
result.Data.Should().Be(userModel);
}
Hope this helps a little 🙂
To use your ApplicationSettings as mocked ApplicationSettings, you must use an expression in the setup method.
Instead of:
IApplicationSettings applicationSettings = new ApplicationSettings() { UserCollectionName = "users" };
_mockApplicationSettings.Setup(app => applicationSettings).Returns(applicationSettings);
Use e.g. this:
IApplicationSettings applicationSettings = new ApplicationSettings() { UserCollectionName= "users" };
_mockApplicationSettings.Setup(app => app.UserCollectionName).Returns(applicationSettings.UserCollectionName);
Edit: Complete Example
private Mock<IDatabaseSettings> _mockDbSettings;
private Mock<IApplicationSettings> _mockApplicationSettings;
private Mock<IMongoClient> _mockClient;
public UserManagmentRepositoryTests()
{
_mockDbSettings = new Mock<IDatabaseSettings>();
_mockApplicationSettings = new Mock<IApplicationSettings>();
_mockClient = new Mock<IMongoClient>();
}
[Fact]
public async Task GetUserWithId_Test()
{
// Arrange
var repo = new Mock<IUserManagementRepository>();
IDatabaseSettings dbSettings = new DatabaseSettings()
{
ConnectionString = "mongodb:connection",
DatabaseName = "dbname"
};
_mockDbSettings.Setup(x => x.ConnectionString).Returns(dbSettings.ConnectionString);
_mockDbSettings.Setup(x => x.DatabaseName).Returns(dbSettings.DatabaseName);
IApplicationSettings applicationSettings = new ApplicationSettings() { UserCollectionName = "users" };
_mockApplicationSettings.Setup(app => app.UserCollectionName).Returns(applicationSettings.UserCollectionName);
MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(dbSettings.ConnectionString);
MongoClient client = new MongoClient(clientSettings);
_mockClient.Setup(c => c.GetDatabase(It.IsAny<string>(), It.IsAny<MongoDatabaseSettings>()))
.Returns <string, MongoDatabaseSettings>((name, settings) => client.GetDatabase(name, settings));
var ctr = new UserManagementRepository(_mockDbSettings.Object, _mockApplicationSettings.Object, _mockClient.Object);
// Act
var result = await ctr.Get("132");
// Assert
result.ResponseCode.Should().NotBe(ResponseCode.Error);
}
I see no reason mocking IApplicationSettings and IDatabaseSettings you should go with :
public class UserManagmentRepositoryTests
{
private Mock<IMongoClient> _mockClient;
public UserManagmentRepositoryTests()
{
_mockClient = new Mock<IMongoClient>();
}
[Fact]
public async Task GetUserWithId_Test()
{
// Arrange
var repo = new Mock<IUserManagementRepository>();
IDatabaseSettings dbSettings = new DatabaseSettings()
{
ConnectionString = "mongodb:connectionstring",
DatabaseName = "dbname"
};
IApplicationSettings applicationSettings = new ApplicationSettings() { UserCollectionName = "users" };
MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(dbSettings.ConnectionString);
MongoClient client = new MongoClient(clientSettings);
_mockClient.Setup(c => client);
var ctr = new UserManagementRepository(dbSettings, applicationSettings, _mockClient.Object);
// Act
var result = ctr.Get("132");
// Assert
//result.StatusCode.Should().NotBeNull();
}
}
Related
I am new to Moq having used Rhino mocks for a while. I am trying to stub a method so that is returns the information I expect but the actual line of code when running the test returns a null reference exception. Am I missing something obvious here? Below is my code:
public void GetResSystemClients(ResSystem resSystem)
{
ResSystemDetail = new ResSystemDetails() {ResSys = resSystem};
//RETURNS NULL REFERENCE EXCEPTION
var resClients = FileReader.ReadFile((c) => c.Where(x =>
x.ReservationSystem.Replace(" ", "").ToLowerInvariant().Contains(resSystem.ToString().ToLowerInvariant())));
ResSystemDetail.ResSystemClients = resClients
.Select(y => new ResSystemClient() {ClientCode = y.ClientCode, ClientName = y.ClientName})
.OrderBy(z => z.ClientCode).ToList();
}
Test Code
[SetUp]
public void SetUp()
{
mockFileReader = new Mock<IClientLookUpFileReader>(MockBehavior.Default);
sut = new ClientLookupModel();
}
[TestCase(ResSystem.Test)]
public void When_GetResSystemCalled_With_ResSystem_Return_CorrectClients(ResSystem system)
{
//Arrange
mockFileReader.Setup(x =>
x.ReadFile(It.IsAny<Func<List<ExtraClientInfo>, IEnumerable<ExtraClientInfo>>>()))
.Returns(new List<ExtraClientInfo>
{
new ExtraClientInfo
{
ClientName = "Test",
ClientCode = "XX"
},
new ExtraClientInfo
{
ClientName = "Test1",
ClientCode = "X1"
},
new ExtraClientInfo
{
ClientName = "Test2",
ClientCode = "X2"
},
});
//Act
sut.GetResSystemClients(system);
}
Read file code
public interface IClientLookUpFileReader
{
T ReadFile<T>(Func<List<ExtraClientInfo>, T> predicateFunc);
}
public class ClientLookUpFileReader : IClientLookUpFileReader
{
private const string filePath = "D:/PersistedModels/ClientInfo.json";
T IClientLookUpFileReader.ReadFile<T>(Func<List<ExtraClientInfo>, T> predicateFunc)
{
if (File.Exists(filePath))
{
var clientInfoList = new JavaScriptSerializer().Deserialize<List<ExtraClientInfo>>(System.IO.File.ReadAllText(filePath));
return predicateFunc(clientInfoList);
}
throw new Exception($"File not found at path {filePath}");
}
}
Brand new both to .net core as well as for unit testing, so please be at my help pointing out where the problem lies in my code.
[TestMethod]
public void RetrieveAsync_AddModel_ReturnsNotEmptyPersonList()
{
// Arrange
var data = new List<Person>();
var mySet = new Mock<DbSet<Person>>();
var defaultHttpContext = new DefaultHttpContext();
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
httpContextAccessorMock.Setup(m => m.HttpContext).Returns(defaultHttpContext);
var myContextMock = new Mock<MyContext>(new DbContextOptions<MyContext>(), httpContextAccessorMock.Object);
myContextMock.Setup(c => c.Set<Person>()).Returns(mySet.Object);
var id = Guid.NewGuid();
var service = new PersonService(myContextMock.Object);
var model = new Person
{
Id = id
};
// Act
var createdModel = service.CreateAsync(model).Result;
data.Add(createdModel);
var findModelById = data.Find(item => item.Id == createdModel.Id).Id;
**var retrievedModel = service.RetrieveAsync(findModelById).Result; // null**
// Assert
**Assert.AreEqual(id, createdModel.Id); // correct, test passes
Assert.AreEqual(id, retrievedModel.Id); // retrievedModel is null, test fails**
}
Generics methods I have to test are as follows:
public async Task<T> CreateAsync(T model)
{
_dbContext.Set<T>().Add(model);
await _dbContext.SaveChangesAsync();
return model;
}
public async Task<T> RetrieveAsync(Guid id)
{
return await _dbContext.Set<T>().FindAsync(id);
}
The question is, why retrievedModel is null?
I want to use DataflowEx to encapsulate this test. I am not sure that my design is correct as I have tried a lot to do it my self but I failed to make the Dataflow to complete as in my test and is not clear how I should post the server, IP combination from the outside.
class HttpClientHandlerFactory : Dataflow<string, HttpClientHandler> {
private readonly TransformBlock<string, WebProxy> _webproxyDataflow;
private readonly TransformBlock<WebProxy, HttpClientHandler> _httpClientHandlerDataflow;
public HttpClientHandlerFactory(IHttpClientHandlerFactoryData handlerFactoryData) : this(DataflowOptions.Default, handlerFactoryData) {
}
public HttpClientHandlerFactory(DataflowOptions dataflowOptions, IHttpClientHandlerFactoryData handlerFactoryData) : base(dataflowOptions){
var dataflowBlockOptions = dataflowOptions.ToExecutionBlockOption();
_webproxyDataflow = new TransformBlock<string, WebProxy>(async s => {
new WebProxy(s);
}, dataflowBlockOptions);
_httpClientHandlerDataflow = new TransformBlock<WebProxy, HttpClientHandler>(proxy => new HttpClientHandler(){Proxy = proxy}, dataflowBlockOptions);
var dataflowLinkOptions = new DataflowLinkOptions() { PropagateCompletion = true };
_webproxyDataflow.LinkTo(_httpClientHandlerDataflow, dataflowLinkOptions, proxy => proxy != null);
var nullTarget = DataflowBlock.NullTarget<WebProxy>();
_webproxyDataflow.LinkTo(nullTarget, dataflowLinkOptions, proxy => proxy == null);
RegisterChild(_webproxyDataflow);
RegisterChild(_httpClientHandlerDataflow);
}
public override ITargetBlock<string> InputBlock => _webproxyDataflow;
public override ISourceBlock<HttpClientHandler> OutputBlock => _httpClientHandlerDataflow;
}
class FactoryData: IHttpClientHandlerFactoryData {
}
[Fact]
public async void MethodName(){
var httpClientHandlers = new BufferBlock<HttpClientHandler>();
var httpClientHandlerFactory = new HttpClientHandlerFactory(new FactoryData());
httpClientHandlerFactory.LinkTo(httpClientHandlers.ToDataflow());
var baseAddresses = new BufferBlock<Uri>();
var transformBlock = new TransformBlock<Tuple<HttpClientHandler, Uri>,HttpClient>(
data => {
WriteLine($"{data.Item2.ToString()}-{Environment.CurrentManagedThreadId}-{((WebProxy) data.Item1.Proxy).Address}");
return new HttpClient(data.Item1){BaseAddress = data.Item2};
});
var joinBlock = new JoinBlock<HttpClientHandler,Uri>();
httpClientHandlers.LinkTo(joinBlock.Target1);
baseAddresses.LinkTo(joinBlock.Target2);
joinBlock.LinkTo(transformBlock,new DataflowLinkOptions(){PropagateCompletion = true});
baseAddresses.Post(new Uri("http://www.serverA.com"));
baseAddresses.Post(new Uri("http://www.ServerB.com"));
httpClientHandlerFactory.Post("127.0.0.1");
httpClientHandlerFactory.Post("127.0.0.2");
joinBlock.Complete();
await transformBlock.Completion;
}
Update
Reading carefully JSteward comment about the missing NullTarget block I realized that my raw block method was incorrect. So here is the correct version.
[Fact]
public async Task MethodName() {
var dataflowLinkOptions = new DataflowLinkOptions() { PropagateCompletion = true };
var httpClientHandlerFactory = new HttpClientHandlerFactory(new HttpClientHandlerFactoryData());
var baseAddresses = new BufferBlock<Uri>();
var transformBlock = new TransformBlock<Tuple<HttpClientHandler, Uri>, HttpClient>(
data => {
WriteLine($"{data.Item2.ToString()}-{Environment.CurrentManagedThreadId}-{((WebProxy)data.Item1.Proxy).Address}");
var httpClient = new HttpClient(data.Item1) { BaseAddress = data.Item2 };
return httpClient;
});
var joinBlock = new JoinBlock<HttpClientHandler, Uri>();
httpClientHandlerFactory.OutputBlock.LinkTo(joinBlock.Target1,dataflowLinkOptions);
baseAddresses.LinkTo(joinBlock.Target2,dataflowLinkOptions);
joinBlock.LinkTo(transformBlock, dataflowLinkOptions);
baseAddresses.Post(new Uri("http://www.serverA.com"));
baseAddresses.Post(new Uri("http://www.ServerB.com"));
httpClientHandlerFactory.Post("127.0.0.1");
httpClientHandlerFactory.Post("127.0.0.2");
httpClientHandlerFactory.Complete();
baseAddresses.Complete();
transformBlock.LinkTo(DataflowBlock.NullTarget<HttpClient>(), dataflowLinkOptions);
await transformBlock.Completion;
}
It completes with this output.
02:51:41.3365|http://www.servera.com/-11-http://127.0.0.1/
02:51:41.3365|http://www.serverb.com/-11-http://127.0.0.2/
Now to migrate to DataflowEx I declared the next class
class HttpClientFactory:Dataflow<string,HttpClient>{
private readonly HttpClientHandlerFactory _httpClientHandlerFactory;
private readonly TransformBlock<Tuple<HttpClientHandler, Uri>, HttpClient> _transformBlock;
private JoinBlock<HttpClientHandler, Uri> _joinBlock;
private BufferBlock<Uri> _baseAddresses;
public HttpClientFactory(IHttpClientFactoryData httpClientHandlerFactoryData) : this(DataflowOptions.Default, httpClientHandlerFactoryData) {
}
public HttpClientFactory(DataflowOptions dataflowOptions, IHttpClientFactoryData handlerFactoryData) : base(dataflowOptions) {
var dataflowLinkOptions = new DataflowLinkOptions() { PropagateCompletion = true };
_httpClientHandlerFactory = new HttpClientHandlerFactory(new HttpClientHandlerFactoryData());
_baseAddresses = new BufferBlock<Uri>();
_transformBlock = new TransformBlock<Tuple<HttpClientHandler, Uri>, HttpClient>(
data => {
handlerFactoryData.Logger.WriteLine($"{data.Item2.ToString()}-{Environment.CurrentManagedThreadId}-{((WebProxy)data.Item1.Proxy).Address}");
return new HttpClient(data.Item1) { BaseAddress = data.Item2 };
});
_joinBlock = new JoinBlock<HttpClientHandler, Uri>();
_httpClientHandlerFactory.OutputBlock.LinkTo(_joinBlock.Target1,dataflowLinkOptions);
_baseAddresses.LinkTo(_joinBlock.Target2,dataflowLinkOptions);
_joinBlock.LinkTo(_transformBlock, dataflowLinkOptions);
RegisterChild(_transformBlock);
RegisterChild(_httpClientHandlerFactory);
RegisterChild(_baseAddresses);
foreach (var baseAddress in handlerFactoryData.BaseAddresses){
_baseAddresses.Post(baseAddress);
}
}
public override ITargetBlock<string> InputBlock => _httpClientHandlerFactory.InputBlock;
public override ISourceBlock<HttpClient> OutputBlock => _transformBlock;
}
and wrote a test that it completes ok with the expected output
[Fact]
public async Task MethodName2(){
var clientFactoryData = new HttpClientFactoryData(new[]{new Uri("http://www.serverA.com")}){Logger = this};
var httpClientFactory = new HttpClientFactory(DataflowOptions.Verbose,clientFactoryData);
httpClientFactory.InputBlock.Post("127.0.0.1");
httpClientFactory.LinkLeftToNull();
httpClientFactory.Complete();
await httpClientFactory.OutputBlock.Completion;
}
02:55:44.8536|[HttpClientFactory1] has 0 todo items (in:0, out:0) at this moment.
02:55:45.0039|[HttpClientFactory1]->[HttpClientHandlerFactory1] completed
02:55:45.0039|http://www.servera.com/-11-http://127.0.0.1/
However in in the above test I used the raw Complete method and awaited on the OutputBlock and not on the DataFlow. The next test that uses the builtin Dataflow method does not complete.
[Fact]
public async Task MethodName2(){
var clientFactoryData = new HttpClientFactoryData(new[]{new Uri("http://www.serverA.com")}){Logger = this};
var httpClientFactory = new HttpClientFactory(DataflowOptions.Verbose,clientFactoryData);
httpClientFactory.InputBlock.Post("127.0.0.1");
httpClientFactory.LinkLeftToNull();
await httpClientFactory.SignalAndWaitForCompletionAsync();
}
Overriding the Complete method to complete the additional BufferBlock makes the test pass.
public override void Complete(){
base.Complete();
_baseAddresses.Complete();
}
I have a class and in each method I am declaring the following lines repeatedly:
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var engines = new ViewEngineCollection();
engines.Add(new FileSystemRazorViewEngine(viewsPath));
How and where do I declare them so that it is available to each method so that I'm not having to write the same line repeatedly inside each method?
public class EmailService
{
public EmailService()
{
}
public void NotifyNewComment(int id)
{
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var engines = new ViewEngineCollection();
engines.Add(new FileSystemRazorViewEngine(viewsPath));
var email = new NotificationEmail
{
To = "yourmail#example.com",
Comment = comment.Text
};
email.Send();
}
public void NotifyUpdatedComment(int id)
{
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var engines = new ViewEngineCollection();
engines.Add(new FileSystemRazorViewEngine(viewsPath));
var email = new NotificationEmail
{
To = "yourmail#example.com",
Comment = comment.Text
};
email.Send();
}
}
You could make them class-level members:
public class EmailService
{
private string viewsPath;
private ViewEngineCollection engines;
public EmailService()
{
viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
engines = new ViewEngineCollection();
engines.Add(new FileSystemRazorViewEngine(viewsPath));
}
public void NotifyNewComment(int id)
{
var email = new NotificationEmail
{
To = "yourmail#example.com",
Comment = comment.Text
};
email.Send();
}
// etc.
}
This would populate the variables once when you create a new EmailService:
new EmailService()
Then any method executed on that instance would use the values which were created at that time.
I have the following three methods in the CompanyApplication class (along with the supporting factories and services listed):
public ResultSet<CompanyDto> AddCompany(CompanyDto companyDto)
{
var result = new CompanyDto();
var company = new Company();
Mapper.Map(companyDto, company);
using (ITransaction t = _transactionFactory.Create())
{
company = _companyService.Add(company);
t.Commit();
}
Mapper.Map(company, result);
return new ResultSet<CompanyDto>(1, new[] { result });
}
public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto)
{
var result = new CompanyContactDto();
var company = new Company();
var contact = new CompanyContact();
Mapper.Map(companyContactDto, contact);
using (ITransaction t = _transactionFactory.Create())
{
var contactCompanies = FindByIdJoin<Company, CompanyDto>(companyContactDto.CompanySK);
Mapper.Map(contactCompanies.Data.First(), company);
company.CompanyContacts.Add(contact);
company = _companyService.Update(company);
t.Commit();
}
Mapper.Map(contact, result);
return new ResultSet<CompanyContactDto>(1, new[] { result });
}
public ResultSet<T_DtoType> FindByIdJoin<T_DbType, T_DtoType>(long id)
{
IAbstractRepository<T_DbType> repository = EnsureRepository<T_DbType>();
T_DbType entity = repository.FindByIdJoin(id);
return (entity == null ? null : MapResultSetToDto<T_DbType, T_DtoType>(entity));
}
There are other objects in play here, which is why the FindByIdJoin has been made a separate method in the CompanyApplication class.
I have set up the testing class with some mocks and an instance of the CompanyApplication class:
private Mock<ICompanyRepository> _mockCompanyRepository;
private Mock<ICompanyDomain> _mockCompanyService;
private Mock<ITransactionFactory> _mockTransactionFactory;
private Mock<ITransaction> _mockTransaction;
private CompanyApplication _companyApplication;
[Setup]
public void SetUp()
{
_mockCompanyRepository = new Mock<ICompanyRepository>(MockBehavior.Strict);
_mockCompanyService = new Mock<ICompanyDomain>(MockBehavior.Strict);
_mockTransactionFactory = new Mock<ITransactionFactory>(MockBehavior.Strict);
_mockTransaction = new Mock<ITransaction>(MockBehavior.Strict);
_companyApplication = new CompanyApplication(
_mockCompanyRepository.Object,
_mockCompanyService.Object,
_mockTransactionFactory.Object);
}
I am successfully able to test the FindByIdJoin and AddCompany methods directly in Moq like this:
[Test]
public void CanFindCompanyByIdJoin()
{
var data = new Company {ObjectId = 1, Name = "Company1"};
_mockCompanyRepository.Setup(x => x.FindByIdJoin(It.Is<long>(arg => arg == data.ObjectId)))
.Returns(data);
var result = _companyApplication.FindByIdJoin<Company, CompanyDto>(data.ObjectId);
Assert.AreEqual(data.ObjectId, result.Data.First().ObjectId);
}
[Test]
public void CanAddCompany()
{
var data = new Company {ObjectId = 1, Name = "Company1"};
_mockCompanyService.Setup(x => x.Add(It.Is<Company>(arg => arg.ObjectId == data.ObjectId)))
.Returns(data);
_mockTransactionFactory.Setup(x => x.Create()).Returns(_mockTransaction.Object);
_mockTransaction.Setup(x => x.Commit());
_mockTransaction.Setup(x => x.Dispose());
var dto = new CompanyDto {ObjectId = 1, Name = "Company1"};
var result = _companyApplication.AddCompany(dto);
_mockCompanyService.Verify(t => t.Add(It.IsAny<Company>()));
}
Those two tests pass just fine. However, I'm having trouble coming up with a test for AddCompanyContact, because it calls FindByIdJoin as part of its flow, and that seems to be getting in the way.
Specifically, is there a way to mock var contactCompanies = FindByIdJoin<Company, CompanyDto>(companyContactDto.CompanySK) in a test for the AddCompanyContact method?
Thanks!
There is two alternatives that i see depending on the amount of work that you want to do.
Wrap that call into a object and instantiate it using a IOC container. This is the one that i feel would take the most effort if you are not using one already.
Turn that call into a Func and make a method without that parameter that does the call. This approach has the disadvantage that the top call will be untestable but will allow access to the rest of the method.
Example Below:
public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto)
{
AddCompanyContact(CompanyContactDto, ()=>
{
return FindByIdJoin<Company, CompanyDto> companyContactDto.CompanySK);
}
}
public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto, Func<WhateverTheMethodReturns> findIdReplacement)
{
var result = new CompanyContactDto();
var company = new Company();
var contact = new CompanyContact();
Mapper.Map(companyContactDto, contact);
using (ITransaction t = _transactionFactory.Create())
{
var contactCompanies = findIdReplacement();
Mapper.Map(contactCompanies.Data.First(), company);
company.CompanyContacts.Add(contact);
company = _companyService.Update(company);
t.Commit();
}
Mapper.Map(contact, result);
return new ResultSet<CompanyContactDto>(1, new[] { result });
}
I was over complicating the problem... since AddCompanyContact calls FindByIdJoin, all I needed to do was mock the same interface that is used in FindByIdJoin.
Lesson learned: mock interfaces, not classes.