How to unit test CRUD (Retrieve)? - c#

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?

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

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

How to add annotation to an entity?

I am attempting to write a unit test for this method:
public List<Annotation> GetNotesByOrderGuid(Guid orderGuid)
{
var result = _xrmServiceContext.SalesOrderSet.Where(x => x.Id == orderGuid); //!!!!! this is returning correctly 1 record, however, it shows NULL for the list of annotations
//do some stuff and return a list of annotations
}
My unit test creates 2 notes, and attaches them to the sales order:
private XrmFakedContext _fakeContext;
[NotNull]
private IOrganizationService _fakeOrganizationService;
[Test]
public void GetNotesByOrderGuid_ExistingRecordHavingNotes_ReturnsListOfThoseNotes()
{
using (var xrmServiceContext = new XrmServiceContext(_fakeOrganizationService))
{
// Arrange
var salesOrderGuid = Guid.NewGuid();
var salesOrder = new SalesOrder { Id = salesOrderGuid };
var note1 = new Annotation
{
Id = Guid.NewGuid(),
NoteText = "this is note1",
ObjectId = new EntityReference(SalesOrder.EntityLogicalName, salesOrderGuid),
ObjectTypeCode = salesOrder.LogicalName
};
var note2 = new Annotation
{
Id = Guid.NewGuid(),
NoteText = "this is note2",
ObjectId = new EntityReference(SalesOrder.EntityLogicalName, salesOrderGuid),
ObjectTypeCode = salesOrder.LogicalName
};
_fakeContext.Initialize(new List<Entity> { salesOrder, note1, note2});
var sut = new SalesOrderService(xrmServiceContext);
// Act
// Assert
Assert.That(sut.GetNotesByOrderGuid(salesOrderGuid), Is.InstanceOf<List<Annotation>>());
}
}
[SetUp]
public void Init()
{
_fakeContext = new XrmFakedContext { ProxyTypesAssembly = Assembly.GetAssembly(typeof(SalesOrder)) };
_fakeOrganizationService = _fakeContext.GetFakedOrganizationService();
}
What am doing incorrectly in adding annotations to my entity?
The reason I ask is because when the unit tests runs this code:
var result = _xrmServiceContext.SalesOrderSet.Where(x => x.Id == orderGuid);
it shows that although there is 1 result, as there correctly should be; it shows that no annotations have been linked to it:

Unit Test Mocking an IHttpActionResult GetId method

I am creating some unit tests for a controller I have however I ran into a problem.
Basically I have the following:-
The Controller Method :-
[ResponseType(typeof(Attrib))]
public IHttpActionResult GetAttrib(int id)
{
var attrib = _attribsRepository.GetAttrib(id);
if (attrib == null)
{
return NotFound();
}
return Ok(attrib);
}
Its a simple Web API 2.0 method.
Then I have the Repository :-
public Attrib GetAttrib(int id)
{
return DbSet.FirstOrDefault(x=>x.Id == id);
}
And finally the Test Method:-
public class AttribsControllerTests
{
public Mock<IAttribsRepository> _attribsRepositoryMock;
public List<Attrib> AttribList;
public AttribsController AttribsController;
[SetUp]
public void Init()
{
_attribsRepositoryMock = new Mock<IAttribsRepository>();
AttribList = new List<Attrib>
{
new Attrib()
{
Id = 1,
AttributeId = "Cro",
AttributeName = "Crossing",
AttributeType = "Tech",
AttributeValue = 1
},
new Attrib()
{
Id = 2,
AttributeId = "Dri",
AttributeName = "Dribbling",
AttributeType = "Tech",
AttributeValue = 2
},
new Attrib()
{
Id = 3,
AttributeId = "Fin",
AttributeName = "Finishing",
AttributeType = "Tech",
AttributeValue = 3
}
};
}
[Test]
public void Get_Check_That_Id1_Returns_Crossing()
{
//Arrange
_attribsRepositoryMock.Setup(t => t.GetStaticAttribs()).Returns(AttribList.AsQueryable());
//Act
var attribsController = new AttribsController(_attribsRepositoryMock.Object);
var result = attribsController.GetAttrib(1) as OkNegotiatedContentResult<Attrib>;
//Assert
Assert.IsNotNull(result);
Assert.AreEqual(AttribList[0].AttributeName, "Cor");
}
}
For some reason, the result is always null, so its not hitting the controller correctly.
Any ideas why this could happen? When debugging, the correct Mock Repository is hitting the controller, and it should have the 3 entries in it.
Any help will be very much appreciated.
You setup GetStaticAttribs but it is used nowhere in the example you showed. You were suppose to setup IAttribsRepository.GetAttrib
Based on your example
[Test]
public void Get_Check_That_Id1_Returns_Crossing() {
//Arrange
var id = 1;
_attribsRepositoryMock.Setup(t => t.GetAttrib(id)).Returns(AttribList[0]);
var attribsController = new AttribsController(_attribsRepositoryMock.Object);
//Act
var result = attribsController.GetAttrib(id) as OkNegotiatedContentResult<Attrib>;
//Assert
Assert.IsNotNull(result);
Assert.IsNotNull(result.Content);
Assert.AreEqual(result.Content.AttributeName, "Crossing");
}

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