C# Integration tests with XUnit, SQL Server Database and transactions - c#

I've been working on adding integration tests with XUnit and SQL Server. I'm facing a challenge: Have a clean database state before and after each test.
This is an example of my test:
[Fact]
public async Task GetAsync_ReturnsTaskOfRoleModelList()
{
using var context = _factory.CreateContext();
_rolesRepository = () => _factory.CreateRoleRepository(context);
await context.Database.BeginTransactionAsync();
await _rolesRepository().CreateAsync(new RoleModel
{
Id = Guid.NewGuid(),
Name = "Role A",
Description = "Role A Description",
CreatedDate = DateTime.Now,
UpdatedDate = DateTime.Now,
Deleted = false,
});
var response = await _client.GetFromJsonAsync<List<RoleModel>>("/api/roles");
response.ShouldNotBeNull();
response.ShouldBeOfType<List<RoleModel>>();
response.Count.ShouldBeGreaterThan(0);
context.ChangeTracker.Clear();
}
First a context is created, and then a repository is also created with that context. Then,
we start the transaction, add the data to the database, send the request and check the response, to finally clear the transaction. Unfortunately this test is failing.
Using this microsoft article as reference: https://learn.microsoft.com/en-us/ef/core/testing/testing-with-the-database.
This line does not create a new Role in the database:
await _rolesRepository().CreateAsync(new RoleModel
{
Id = Guid.NewGuid(),
Name = "Role A",
Description = "Role A Description",
CreatedDate = DateTime.Now,
UpdatedDate = DateTime.Now,
Deleted = false,
});
If I remove the lines related to the database transaction, the test passes and the role is added.
How can I solve this?

Related

Planner Tasks Graph API: Update Task Issue

We have developed an application which calls Update Task Planner Graph API to update task in Planner App. The API was working fine until some recent change in the MS docs and now it keeps throwing below error.
A type named 'microsoft.taskServices.plannerCheckListItemCollection' could not be resolved by the model. When a model is available, each type name must resolve to a valid type.
Below is the code for creating and updating the Task using Graph API.
var newTask = new PlannerTask
{
PlanId = "planID",
BucketId = "bucketID",
Title = "title"
};
Logger.LogInfo(userLogs + "Task object created, calling Graph API");
var taskResponse = await graphClient.Planner.Tasks.Request().AddAsync(newTask);
PlannerTaskDetails taskDetails = new PlannerTaskDetails
{
Checklist = new PlannerChecklistItems { AdditionalData = checkListItems }
};
Logger.LogInfo(userLogs + "Getting Details of created Task");
PlannerTaskDetails getTaskDetails = await graphClient.Planner.Tasks[taskResponse.Id].Details.Request().GetAsync();
var eTagId = getTaskDetails.GetEtag();
Logger.LogInfo(userLogs + "Updating Task");
await graphClient.Planner.Tasks[taskResponse.Id].Details.Request()
.Header("Prefer", "return=representation")
.Header("If-Match", eTagId)
.UpdateAsync(taskDetails);
Code snippet for CheckListItems:
Dictionary<string, Object> checkListItems = new Dictionary<string, object>();
checkListItems.Add(key, new PlannerChecklistItem
{
Title = "title",
IsChecked = True
});
Also proper App Permissions in Azure are already given as this was working fine till last month.
This was a bug, and the issue should be resolved now.

Unit test for API Controller in ASP.NET Core 3.1 returning a wrong status code

I'm writing a unit test for an API Controller performing delete action.
Here's the Delete Action
public IActionResult DeleteSubGenre(Guid subGenreId)
{
if (!_genreRepo.SubGenreExist(subGenreId))
{
return NotFound();
}
var genreObj = _genreRepo.SubGenre(subGenreId);
if (!_genreRepo.DeleteSubGenre(genreObj))
{
ModelState.AddModelError("", $"Something went wrong when deleting the record {genreObj.Name}");
return StatusCode(500, ModelState);
}
return NoContent();
}
The unit test for this action is written as
[Fact]
public void DeleteSubGenre_Returns_NoContentResult()
{
// Arrange
var subGenreRepositoryMock = new Mock<ISubGenreRepository>();
var subGenreIMapperMock = new MapperConfiguration(config =>
{
config.AddProfile(new MovieMapper());
});
var subGenreMapper = subGenreIMapperMock.CreateMapper();
SubGenresController subGenreApiController = new SubGenresController(subGenreRepositoryMock.Object, mapper: subGenreMapper);
var subGenreDto = new SubGenreDTO()
{
Name = "Adult Content",
DateCreated = DateTime.Parse("15 May 2015"),
Id = Guid.NewGuid(),
GenreId = Guid.NewGuid(),
Genres = new GenreDTO()
};
// Act
var subGenreResult = subGenreApiController.DeleteSubGenre(subGenreDto.Id);
var noContentResult = subGenreResult as NoContentResult;
// Assert
Assert.False(noContentResult.StatusCode is StatusCodes.Status204NoContent);
}
While debugging the test i noticed that subGenreResult was returning a status code of 404 instead of 204. I can seem to get a hang over it. I'll be glad to get plausible solution to this.
You have to setup your mock to drive the execution of your test case.
For example if you want to go through this line: if (!_genreRepo.SubGenreExist(subGenreId))
then you have to setup the following mock behaviour:
subGenreRepositoryMock.Setup(repo => repo.SubGenreExist(It.IsAny<Guid>)).Returns(true);
To reach this line: return NoContent(); you might need to setup the other two methods as well to drive your test case.

Validation failed for one or more entities issue

This is truly one of the strangest issues I've run into.
I have a Web API which uses EF. I have an audit table which takes an ApplicationUser. I create the new object, add it to the collection and then call SaveChangesAsync(). The weird part is, I get "User name MyUserName is already taken." error.
using (var context = new ApplicationDbContext())
{
var user = context.Users.Single<ApplicationUser>(x => x.UserName == model.UserName);
var sid = context.SessionIds.FirstOrDefault(x => x.Id == model.SessionId);
var audit = new Audit
{
Data = model.Data,
User = user,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};
context.Audits.Add(audit);
await context.SaveChangesAsync();
}
Update
This code has been working for years. The difference is I upgrade from .NET 4.5 to .NET 4.61
Update 2
I also tried the following but still receive the same error
[ForeignKey("User")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
Update 3
Trying to track this issue down I call
var entries = context.ChangeTracker.Entries();
It returns several entries, 1 for each object, including User. User shows Added and another as Unchanged. I can't figure out how this is happening.
In addition, I added the following before making any changes but there's no effect.
context.Configuration.AutoDetectChangesEnabled = false;
Since You are adding the complete user object in Audit , so SaveChangesAsync will save a new Entry for Audit and User also and since a user with same username already exists that's why you are getting this error. What you should do is just assign just the UserId (Whatever is referral key in Audit table for User) in Audit object
var audit = new Audit
{
Data = model.Data,
UserId = user.Id,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};

Autofac + Moq: Setup and Verify. (C#)

So we have a 3 tier Web API project which I am attempting to implement some tests for.
We are using Autofac as our IoC container, and its integration library with Moq to implement some unit testing.
Start with some code:
[TestMethod]
public void Get()
{
using (var mock = AutoMock.GetLoose())
{
// DATA LAYER
// mock some contact objects up
var contactsList = new List<CONTACT>
{
new CONTACT { CONTACT_ID = 1, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock1", SURNAME = "Mock1" },
new CONTACT { CONTACT_ID = 2, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock2", SURNAME = "Mock2" },
new CONTACT { CONTACT_ID = 3, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock3", SURNAME = "Mock3" },
new CONTACT { CONTACT_ID = 4, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock4", SURNAME = "Mock4" },
new CONTACT { CONTACT_ID = 5, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock5", SURNAME = "Mock5" },
}.AsQueryable();
var contactsSet = new Mock<DbSet<CONTACT>>();
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.Provider).Returns(contactsList.Provider);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.Expression).Returns(contactsList.Expression);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.ElementType).Returns(contactsList.ElementType);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.GetEnumerator()).Returns(contactsList.GetEnumerator());
// mock that context
var mockedContext = new Mock<ExampleEntities>();
// set up context so that the contactsSet above is returned when queried.
mockedContext.Setup(c => c.CONTACTS).Returns(contactsSet.Object);
mock.Provide<IContactsRepository, ContactsRepository>(new TypedParameter(typeof(KCMEntities), mockedContext.Object));
mock.Provide<IContacts, Contacts>();
// Instantiate the controller we are performing the test on
var testController = mock.Create<ContactsController>();
// act
ContactListResult contactListResult = testController.Get();
// assert
Assert.AreEqual(true, contactListResult.Success);
Assert.AreEqual(5, contactListResult.ContactList.Count());
Assert.AreEqual("Mock1", contactListResult.ContactList.First().Forename);
Assert.AreEqual(1001, contactListResult.ContactList.First().Id); // The businesslogic layer has been doctored to add 1000 to the id...just to test it is passing through this layer..
mock.Mock<IContactsRepository>().Verify(x => x.GetContacts(), Times.Once());
}
}
We're not sure if this is really meant to be done but we are trying to write the test to test/mock the action of calling a controller down through all the layers of the application.
The IContactsRepository and the IContacts are the services sitting in the data layer and a business logic layer respectively.
We need to mock the dbcontext at the data layer to return our mocked data, and make sure autofac knows what components to provide for both services when resolving:
mock.Provide<IContactsRepository, ContactsRepository>(new TypedParameter(typeof(KCMEntities), mockedContext.Object));
mock.Provide<IContacts, Contacts>();
We're setting up the expectation that the mocked context is to be passed into the data layer when it is resolved. This is an overridden constructor on the concrete implementation of the IContactsRepository (tried a non overidden same issues). The default constructor simply news up a new dbcontext, no params.
This seems to work up to a point.
All asserts are fine until we try a verify:
mock.Mock<IContactsRepository>().Verify(x => x.GetContacts(), Times.Once());
This throws an error:
"Unable to cast object of type 'Data.Repositories.ContactsRepository' to type 'Moq.IMocked``1[Data.Interfaces.IContactsRepository]'."
Now for obvious reasons we want to use verify to verify certain methods are called on the components/services we have.
We've tried many variations of setup e.g. mock.Mock>IContacts>().Setup(.... etc etc, and not managed to work it out/set it up properly.
Essentially we are either doing something basically flawed in trying to test like this, or hopefully we are just doing incorrect setup.
Any suggestions on how to achieve this?
Thanks in advance for your help.

Unit testing in RavenDB how to do it properly

So my problem is as follows...for a project we are bulding an api using RavenDb and Nancy. So my question is about unit testing...we use embeded db, which runs in memory as suggested many times. , how to proper unit test end points. For example, we have a endpoint create account. For that we need to have a user so he can create account. What is the best way to simulate that?
Currently we do it like this:
[Test]
public void UserCanAddAccountToCompany()
{
var user =
new User
{
Name = Guid.NewGuid().ToString(),
Email = Guid.NewGuid().ToString(),
Pwd = "password",
CompanyReference = new CompanyReference { Id = Guid.NewGuid().ToString(), Name = Guid.NewGuid().ToString() }
};
var response = new TestBrowser<User>("User/SignUp", user).Response;
var paramUserAccount = new ParamUserAccount()
{
User = response.Body.DeserializeJson<Result>().User,
Account = new Account() { Name = Guid.NewGuid().ToString() }
};
var response2 = new TestBrowser<ParamUserAccount>("account/create", paramUserAccount).Response;
var res = response2.Body.DeserializeJson<Result>();
Assert.NotNull(res.Account.Id);
Assert.NotNull(res.Account.Name);
}
So we create a user , call user signup end point and then take the params from response and call creat accoutn end point. The obvious problem with this approach is that if you do a change in signup endpoint and for some reason you break it, all tests like this will fail.
So my question is...what's the right approach on that?
You are intergration testing, not unit testing. To properly unit test, you need to test without the db by using mocking objects.

Categories

Resources