Encapsulating with DataflowEx - c#

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();
}

Related

Unit Test mongoDb with .net core

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();
}
}

Moq keeps throwing null reference exception

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}");
}
}

How to unit test CRUD (Retrieve)?

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?

Unit test: Setup read/write Session variable using Moq

I have a function which based on some internal logic, stores a redirect string in a session variable (Redirect_To). I am trying to write a unit test which checks the value of this session variable once the action called has completed.
I am using Moq to mock up some of my properties and functions. However, whenever I set the Session variable (Redirect_To) up with Moq, it must set it as read only because I can no longer assign a value to it which leads my unit test to fail.
Here is my unit test:
public async Task Index_ClientTypeIsRPI_ExpectCorrectRedirect()
{
var sessionDict = new Dictionary<string, string>
{
{"CID", "124" },
{"UserName", "AK92630" },
{"REDIRECT_TO", "www.google.com" }
};
var controllerContext = HttpContextManager.ReturnMockControllerContextWithSessionVars(sessionDict);
var mockIdentity = new Mock<IIdentity>();
mockIdentity.Setup(x => x.Name).Returns("TestName");
controllerContext.Setup(x => x.HttpContext.User.Identity).Returns(mockIdentity.Object);
controllerContext.Setup(x => x.HttpContext.User.IsInRole(UserLevel.Subrep.GetDescription())).Returns(true);
controllerContext.Setup(x => x.HttpContext.Request.Browser.Browser).Returns("InternetExplorer");
_controller.ControllerContext = controllerContext.Object;
_iMockManifestManager.Setup(x => x.ReturnSingleMainDataRecordViaTransNoCIDAndUserNameAsync(It.IsAny<string>, It.IsAny<string>, It.IsAny<string>)).ReturnsAsync(New MainData());
var transNo = "Asdf";
await _controller.Index(transNo, true, "sd", "dg");
Assert.AreEqual("www.facebook.com", _controller.HttpContext.Session["REDIRECT_TO"]);
}
ReturnMockControllerContextWithSessionVars (Function which sets my session variables)
internal static Mock<ControllerContext> ReturnMockControllerContextWithSessionVars(Dictionary<string, object> sessionKeysAndValues):
{
var fakeHttpContext = HttpContextManager.CreateLocalAuthenticatedUserWithMockHttpContext();
var controllerContext = new Mock<ControllerContext>;
controllerContext.Setup(x => x.HttpContext).Returns(fakeHttpContext.Object);
foreach (var item in sessionKeysAndValues)
{
controllerContext.Setup(x => x.HttpContext.Session[item.Key]).Returns(item.Value);
}
return controllerContext;
}
Action:
public async Task Index(string transNo, bool? isRechase, string clientID, string clientType)
{
switch (clientType.ToUpper())
{
case "RPI":
Session["REDIRECT_TO"] = "www.reddit.com";
break;
case "LM":
Session["REDIRECT_TO"] = "www.bbc.co.uk";
default:
Session["REDIRECT_TO"] = "www.stackoverflow.com";
break;
}
//Do async stuff
return null;
}
Does anyone know how to change my code so when I setup Session["Redirect_To"] it remains read/write?
Also, I have converted this from VB.NET but I am fluent in both languages so if I have made any syntax errors it's from writing it free-hand. If that's the case, just let me know.
Thanks
Create a Stub to use for the session as moq will have difficulty setting up the collection used to hold values in the indexed property. The other dependencies should work as expected with moq.
class HttpSessionStateMock : HttpSessionStateBase {
private readonly IDictionary<string, object> objects = new Dictionary<string, object>();
public HttpSessionStateMock(IDictionary<string, object> objects = null) {
this.objects = objects ?? new Dictionary<string, object>();
}
public override object this[string name] {
get { return (objects.ContainsKey(name)) ? objects[name] : null; }
set { objects[name] = value; }
}
}
Using the example method under test from your original question base on "RPI" being the clientType the Following Test was exercised to completion and passed as expected.
[TestMethod]
public async Task Index_ClientTypeIsRPI_ExpectCorrectRedirect() {
//Arrange
var mock = new MockRepository(MockBehavior.Loose) {
DefaultValue = DefaultValue.Mock,
};
var session = new HttpSessionStateMock();//<-- session stub
var controllerContext = mock.Create<ControllerContext>();
controllerContext.Setup(_ => _.HttpContext.Session).Returns(session);
var _controller = new MyController() {
ControllerContext = controllerContext.Object
};
var transNo = "Asdf";
var clientID = "123";
var clientType = "RPI";
var sessionKey = "REDIRECT_TO";
var expected = "www.reddit.com";
//Act
await _controller.Index(transNo, true, clientID, clientType);
var actual = _controller.HttpContext.Session[sessionKey].ToString();
//Assert
Assert.AreEqual(expected, actual);
}

Mock method of system-under-test with Moq

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.

Categories

Resources