I have created a Web API using ASP.NET Core 3.1 and EF Core 5.0. Data is stored in a MSSQL database.
I have implemented UnitOfWork and repository pattern.
The UnitOfWork class has a method used to create transactions and it look like this:
//UnitOfWork
public async Task ExecuteTransaction(Func<Task> insideTransactionFunction)
{
var transaction = await _context.Database.BeginTransactionAsync();
try
{
await insideTransactionFunction();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
}
Im trying to write tests, for service classses that uses the UnitOfWork class to modify the data in the SQL database. Fx when adding a user using the UserService:
//UserService
public async Task<User> AddUserAsync(User user)
{
await _unitOfWork.ExecuteTransaction(async () =>
{
//Add user locally to generate the local id
_unitOfWork.UserRepository.Add(user);
await _unitOfWork.SaveChangesAsync();
//...Some logic that uses the generated user id...
if(string.IsNullOrWhiteSpace(email.User) == false)
{
//Inviting the user returns the generated Azure User id
user.AzureUserId = _azureService.InviteUser(user.Email);
await _unitOfWork.SaveChangesAsync();
}
}
return user;
}
A test for the above method could look like this:
//UserServiceTests
public async Task AddUserAsync_UserQualifiesForAzureInvite_AzureUserIdSetOnCreatedUser()
{
//Arrange
var unitOfWork = Substitute.For<IUnitOfWork>();
var azureService = Substitute.For<IAzureService>();
var uut = new UserService(unitOfWork, azureService);
var newUser = new User { ... some setup ... };
var azureUserId = Guid.NewGuid();
azureService.InviteUser(null).ReturnsForAnyArgs(Task.FromResult(azureUserId));
//Act
var createdUser = await _uut.AddUserAsync(newUser); //Returns right away
//Assert
Assert.That(createdUser.AzureUserId, Is.EqualTo(azureUserId));
}
The problem is that the transaction created with UnitOfWork is ignored and the user is returned straight away.
public async Task<User> AddUserAsync(User user)
{
//Skipped from here...
await _unitOfWork.ExecuteTransaction(async () =>
{
//
}
//...To here
return user;
}
So my question is: How can i mock the UnitOfWork class and still be able to test code wrapped in the the transaction?
My architecture was fundamentally flawed. Check out the comments and links made by Panagiotis Kanavos.
The solution was to get rid of UnitOfWork and the repositories and it seems testable now.
Related
I am using EF Core in a projet to get stored procedure calling. In my context i have the following :
public class MyContext : DbContext
{
public DbQuery<User> UserQuery { get; set; }
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
}
And i call the stored procedure like this :
public virtual async Task<User> GetUserAsync(string name)
{
return await MyContext.Query<User>()
.FromSql($"EXEC [dbo].[GetUser], #Login = {name}")
.FirstOrDefaultAsync();
}
Code is working fine. I need to test this method in unit tests, i'm using InMemoryDatabase to mock my context MyContext like this :
[Fact]
public async Task GetUserAsync_should_return_first_user_with_login_and_password_if_exists()
{
// Arrange
var users = new List<User>
{
new User()
{
Login = "test#outlook.fr",
Password = "pass1",
},
};
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase(databaseName: "BddName")
.Options;
var context = new MyContext(options);
var loginProvider = A.Fake<LoginProvider>(opts => opts.WithArgumentsForConstructor(() => new LoginProvider(context)));
// Act
// Assert
context.Dispose();
}
And i have no idea how can i set my list into the result of the stored procedure called from DbQuery. I tried to follow this article : https://nodogmablog.bryanhogan.net/2017/11/unit-testing-entity-framework-core-stored-procedures/ but it works for DbSet only and not DbQuery.
I need some advices for this case.
Thanks in advance.
The link in the OP does apply to the DbQuery type as well, as you're mocking the provider. Both DbSet and DbQuery work in the same way in this regard.
See https://stackoverflow.com/a/56940311/2975810 for a previous answer on the topic.
I have a business class that manage a USER entity.
In this class I have a method to return a single user by id:
public Utente GetUser(int id)
{
var utente = _userDataManager.GetUserById(id);
return _mapper.Map<Utente>(utente);
}
_userDataManager is an interface, IUSERDATAMANAGER, and it has implemented with a DAL class; GetUserById return a user or null (search made with EF6).
_mapper is a IMAPPER interface (automapper).
The method return is the mapped object.
I have two question:
Does it make sense to test this method?
Should I mock both the object?
A black-boxed example will be appreciated.
Does it make sense to test this method?
If it is worth writing the code it is worth testing the code.
Should I mock both the object?
When testing a subject under test, you mock the dependencies that would allow the test to be exercised to completion.
For example
public void GetUser_Should_Return_Utente() {
//Arrange
var userId = 2;
var user = new User {
UserId = userId,
//... other properties
};
var userDataManagerMock = new Mock<IUserDataManager>();
userDataManagerMock.Setup(_ => _.GetUserById(userId)).Returns(user);
var expected = new Utente {
Id = user.Id,
//...other properties
}
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(_ => _.Map<Utente>(It.IsAny<object>())).Returns(expected);
var subject = new MyBusinessClass(userDataManagerMock.Object, mapperMock.Object);
//Act
var actual = subject.GetUser(userId);
//Assert
Assert.Equal(expected, actual);
}
In the above code the user data manager and the mapper a mocked and injected into the subject when testing the GetUser method.
This is an isolated unit test of the above method and shows the the current implementation of that method will flow to completion provided the dependencies perform as expected during invocation.
I have an API Controller which publishes a command using NServiceBus. I am using NUnit and NSubstitute for testing. I want to test that certain properties from the model are populated on the command
Here is my controller with a route.
[RoutePrefix("api/fileService")]
public class FileServiceController : ApiController
{
[HttpPost]
[Route("releasefile")]
public async Task<IHttpActionResult> ReleaseFile(FileReleaseAPIModels.ReleaseFileModel model)
{
var currentUser = RequestContext.Principal?.Identity as ClaimsIdentity;
if (model.FileType.Equals("ProductFile"))
{
_logger.Info($"Releasing Product files for date: {model.FileDate.ToShortDateString()} ");
_bus.Send<IReleaseProductFiles>("FileManager.Service", t =>
{
t.FileId = Guid.NewGuid();
t.RequestedDataDate = model.FileDate;
t.RequestingUser = currentUser?.Name;
t.RequestDateTime = DateTime.Now;
});
}
return Ok();
}
}
In my test, I substitute(mock) Ibus and try to validate the call received. Here is the test method:
[Test]
public async Task TestReleaseProductsFile()
{
var bus = Substitute.For<IBus>();
var dbContent = _container.Resolve<IFileManagerDbContext>();
var apiContext = new FileServiceController(bus, dbContent);
//Create a snapshot
var releaseDate = DateTime.Now.Date;
var result = await apiContext.ReleaseFile(new ReleaseFileModel
{
FileDate = releaseDate,
FileType = "ProductFile"
});
Assert.That(result, Is.Not.Null, "Result is null");
Assert.That(result, Is.TypeOf<OkResult>(), "Status code is not ok");
bus.Received(1)
.Send<IReleaseProductFiles>(Arg.Is<string>("FileManager.Service"), Arg.Is<Action<IReleaseProductFiles>>(
action =>
{
action.FileId = Guid.NewGuid();
action.RequestedDataDate = releaseDate;
action.RequestingUser = String.Empty;
action.RequestDateTime = DateTime.Now;
}));
}
This results in error - even though the message is actually sent. Here is the error message:
NSubstitute.Exceptions.ReceivedCallsException : Expected to receive exactly 1 call matching:
Send<IReleaseProductFiles>("Capelogic.Service", Action<IReleaseProductFiles>)
Actually received no matching calls.
Received 1 non-matching call (non-matching arguments indicated with '*' characters):
Send<IReleaseProductFiles>("Capelogic.Service", *Action<IReleaseProductFiles>*)
I am obviously missing something obvious here.
The problem here is with the Action<IReleaseProductFiles> argument to Send -- we can't automatically tell if two different actions are the same. Instead, NSubstitute relies on the references being equivalent. Because both the test and the production code create their own Action instance, these will always be different and NSubstitute will say the calls don't match.
There are a few different options for testing this. These examples relate to Expression<Func<>>, but the same ideas apply to Action<>s.
In this case I'd be tempted to test this indirectly:
[Test]
public async Task TestReleaseProductsFile()
{
var bus = Substitute.For<IBus>();
var returnedProductFiles = Substitute.For<IReleaseProductFiles>();
// Whenever bus.Send is called with "FileManager.Service" arg, invoke
// the given callback with the `returnedProductFiles` object.
// We can then make sure the action updates that object as expected.
bus.Send<IReleaseProductFiles>(
"FileManager.Service",
Arg.Invoke<IReleaseProductFiles>(returnedProductFiles));
// ... remainder of test ...
Assert.That(result, Is.TypeOf<OkResult>(), "Status code is not ok");
Assert.That(returnedProductFiles.FileId, Is.Not.EqualTo(Guid.Empty));
Assert.That(returnedProductFiles.RequestedDataDate, Is.EqualTo(releaseDate));
Assert.That(returnedProductFiles.RequestingUser, Is.EqualTo(String.Empty));
}
I'd recommend having a look through the previously mentioned answer though to see if there is a better fit for your situation.
I have a middleware method that requires context.Authentication.User.Identity.Name to be resolved for proper execution. However, when writing a unit test these properties are obviously null as no sign-in has occurred. I am not using Oauth or anything authentication related in this middleware (beyond the obvious name property), as it should be handled elsewhere in another middleware (to promote re-use/flexibility of the component I am developing). Is there a way to mock/fake this value so I can run my test? I have tried everything I can think of to fake a sign-on and I am just stuck at this point. To be clear the middleware needs the value not a webapi call or the like.
//Arrange
var resolver = A.Fake<IDependencyResolver>();
A.CallTo(() => resolver.GetService(typeof(ISomeService))).Returns(new TestService());
using (var server = TestServer.Create(app =>
{
app.UseMyMiddleware(new MyMiddlewareOptions()
{
DependencyResolver = resolver
});
app.Run(async ctx =>
{
await ctx.Response.WriteAsync(ctx.Request.Path.Value);
});
}))
{
//Act
var response = await server.CreateRequest("/").GetAsync();
//Assert
A.CallTo(() => resolver.GetService(typeof(ISomeService)))
.MustHaveHappened(Repeated.Exactly.Once);
Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
//Etc.
}
So here is one way I suppose not thrilled with it but it does the job. I will wait to accept as I imagine there should be a better way.
public class TestFakeLoginMiddleware : OwinMiddleware
{
public TestFakeLoginMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
var identity = A.Fake<IIdentity>();
A.CallTo(() => identity.Name).Returns("TEST#domain.local");
var user = new ClaimsPrincipal(identity);
context.Request.Context.Authentication.User = user;
await Next.Invoke(context);
}
}
A bit late, I know, but could you not just create a new ClaimsIdentity?
public override async Task Invoke(IOwinContext context)
{
var identity= new ClaimsIdentity(new List<Claim> {
new Claim(ClaimTypes.Name, "TEST#domain.local")
});
var user = new ClaimsPrincipal(identity);
context.Request.Context.Authentication.User = user;
await Next.Invoke(context);
}
I'm trying to learn about Unit Testing with the new VS2013 Default MVC w/ Authentication Project. One of the first things I want to test is registering a user. (I know I probably don't need to unit test this since it's already MS tested code but I want to use this to understand the basics). I've also heard that the new Membership code is more 'testable' so I don't need to create my own membership interfaces, etc...
I'm using NSubstitute as a faking framework.
Looking at the Account Controller -> Register() async method
namespace WebApplication1.Controllers
{
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
...
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
If I wanted to write a simple test (ex. Register_RegisterValidUser), how would I do this? I know I need to substitute for the UserManager somehow but this did not work for me:
var substitute = Substitute.For<UserManager<ApplicationUser>>();
I also understand that I need to bypass the async Task<> functions using Task.FromResult but I'm not sure how to return a valid objects from the CreateAsync() and the SigninAsync() methods.
Can somebody help with some sample test code? Many thanks!
To mock the user manager with NSubstitute you have to use this:
var userStore = Substitute.For<IUserStore<ApplicationUser>>();
var userManager = Substitute.For<UserManager<ApplicationUser>>(userStore);
Now you can fake the methods result too. For example:
userManager.FindByNameAsync(Arg.Any<string>()).Returns(Task.FromResult(new ApplicationUser()));
Hope this help you.
Test Guidance
You can Unit Test couple of behaviours, but I will just show you the direction for a single Unit test that just verify whether receive the correct RedirectToAction value. For example,
return RedirectToAction("Index", "Home");
Improving the Testability
In your question you mentioned
I've also heard that the new Membership code is more 'testable' so I
don't need to create my own membership interfaces, etc..
While this is true, I would make a small adjustment so we can make your implementation bit more testable. This way your Unit Test can purely concentrate on the specific behaviour your going test. In other words we can make you SUT (System Under Test) more testable.
await SignInAsync(user, isPersistent: false);
I believe SignInAsync is a private method. There should be some behavior in this method you can probably extract out to a seperate implementation which you can inject in to the SUT. We can call this ISignInManager
public interface ISignInManager {
Task SignInAsync(ApplicationUser user, bool isPersistent);
}
The benefit of this is that now you can inject the behaiovr of an ISignInManager to perform the SignIn tasks and your SUT become more testable. You should see that there will be less mocking/stubbing in your Unit test and make your test easier to write and understand.
Unit Test
You can take the advantage of new async/await usage of the MSTest method. This simplifies complicated and unreliable tests which we used to write.
A Unit Test that verify the correct redirect route controller/action methods, can be written as below.
[TestMethod]
public async Task Register_RegisterValidUser_EnsureRedirectToIndexActionHomeController()
{
// Arrange
var userManagerStub = Substitute.For<IUserManager<ApplicationUser>>();
var tcs = new TaskCompletionSource<IdentityResult>();
tcs.SetResult(new IdentityResult(true));
userManagerStub.CreateAsync(Arg.Any<ApplicationUser>(), Arg.Any<string>()).Returns(tcs.Task);
var signInManagerStub = Substitute.For<ISignInManager>>();
signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));
var sut = new AccountController(userManagerStub) { SignInManager = signInManagerStub.Object };
// Act
var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;
// Assert
Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
}
The above uses NSubstitute as the isolation framework, but if anyone interested in the Moq version, please see below.
[TestMethod]
public async Task Register_RegisterValidUser_EnsureRedirectToIndexHome()
{
// Arrange
var userManagerStub = new Mock<IUserManager<ApplicationUser>>();
var tcs = new TaskCompletionSource<IdentityResult>();
tcs.SetResult(new IdentityResult(true));
userManagerStub.Setup(s => s.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>())).Returns(tcs.Task);
var signInManagerStub = new Mock<ISignInManager>();
signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));
var sut = new AccountController(userManagerStub.Object) {SignInManager = signInManagerStub.Object};
// Act
var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;
// Assert
Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
}