How to test a controller that uses IMemoryCache? - c#

I have a controller with the definition:
public async Task<ResponseDto> MethodNameController(List<string> identifiers)
{
if(!_cache.TryGetValue(key, out IDictonary<string, string> result))
{
result = await service.GetMethodService(activityContextObject, identifiers)
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(80))
.SetAbsoluteExpiration(TimeSpan.FromSeconds(120))
.SetSize(1024);
_cache.Set(key, result, cacheEntryOptions);
}
return new ResponseDto{ //ResponseDto Object split into many fields};
}
The constructor is as below
public Controller(serviceName, httpContextAccessor, IOptionsSnapshot_config, IMemoryCache)
{
_serviceName = serviceName,
_httpContextAccessor = httpContextAccessor,
_config = IOptionsSnapshot_config
_cache = IMemoryCache
}
I'm facing difficulty in unit testing this method. I followed this blog, this SO answer and many more trynig to replicate them in my scenario and failed. I tried passing regular memory cache object(because I'm unable to mock it) but invoking controller returns error slidingExpirationTime should be positive, current time set is 00:00:00 I'm unable to set the sliding expiration in test classes.
Can anyone guide me to correct resources or tell me how I can test this method. I'm new to writing xUnit tests, so any help will be great(For learning moq etc).

The easiest would be to mock it using some libraries, like AutoFixture, Moq.
Generally you mock interfaces as follows:
var fixture = new Fixture() //creates objects that has fine logic in creating objects
.Customize(new AutoMoqCusotmization); // customize the fixture to automoq interfaces
var cacheMock = fixture.Create<Mock<IMemoryCache>>();
// use cacheMock.Setup method
another, simplier option would be to use directly Mock:
var cacheMock = new Mock<IMemoryCache>();
// use cacheMock.Setup method
If you can not use any of the above you can define dummy implmentation for the service, like:
internal class DummyMemoryCache : IMemoryCache
{
// implement interface
)
and use it in place of IMemoryCache.

Related

Setting up an Autofixture for In-memory DbContext

I am currently trying to use Autofixture to create a pre-defined fixture as an implementation of ICustomization for ApplicationDbContext using In-Memory provider.
public class ApplicationDbContextFixture : ICustomization
{
public void Customize(IFixture fixture)
{
var specimenFactory = new SpecimenFactory<ApplicationDbContext>(CreateDbContext);
fixture.Customize<ApplicationDbContext>(
composer =>
composer.FromFactory(specimenFactory)
);
}
/// <summary>
/// Private factory method to create a new instance of <see cref="ApplicationDbContext"/>
/// </summary>
private ApplicationDbContext CreateDbContext()
{
var dbContextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase("SomeDatabaseName")
.Options;
var dbContext = new ApplicationDbContext(dbContextOptions);
return dbContext;
}
}
Then, I will apply that customization to my Fixture as follows:
[Fact]
public void TestAddUsersToEmptyDatabase()
{
// Arrange
// Fixture for ApplicationDbContext
var fixture = FixtureFactory.CreateFixture();
var applicationDatabaseFixture = new ApplicationDbContextFixture();
fixture.Customize(applicationDatabaseFixture);
// Fixture for users
var randomUser = fixture.Create<AppUser>();
var normalUser = fixture.Create<AppUser>();
var adminUser = fixture.Create<AppUser>();
// Act & Assert
// Run the test against one instance of the context
// Use a clean instance of the context for each operation too
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
Assert.Empty(dbContext.Users);
dbContext.Users.Add(randomUser);
dbContext.SaveChanges();
}
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
dbContext.Users.AddRange(normalUser, adminUser);
dbContext.SaveChanges();
}
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
Assert.NotEmpty(dbContext.Users);
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == randomUser.Id));
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == normalUser.Id));
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == adminUser.Id));
}
}
FixtureFactory.CreateFixture implementation
/// <summary>
/// Factory method to declare a single <see cref="IFixture"/> for unit tests applications
/// </summary>
internal static class FixtureFactory
{
internal static IFixture CreateFixture()
{
var fixture = new Fixture().Customize(
new AutoMoqCustomization { ConfigureMembers = true });
return fixture;
}
}
Now in my unit test, asserting the Assert.Empty(dbContext.Users); will throw System.NotImplementedException : The method or operation is not implemented. because the DbSet<AppUser> Users generated from Autofixture is a DynamicProxy.
See image dbContext.Users as DynamicProxy
Oddly enough if I inspect the breakpoints from the factory method (ie. CreateDbContext()) called from the fixture.Create<ApplicationDbContext>(), the DbSet Users is of the expected type.
See image dbContext.Users as InternalDbSet
Optionally, I do aware that I can replace all the usage of dbContext.Users to dbContext.Set<User>() and that would make the unit test pass but the problem is that in the actual class, I am using the dbContext.Users for IQueryables and database operations, so I still need to stick with it if possible.
Hence, I would need help to know why does AutoFixture used my factory method to generate the instance for my ApplicationDbContext but all the DbSet<> properties inside it are mocked when resolved by the ISpecimenBuilder. Is there a way to remedy this?
I've post the similar question in their Github but it has been not active recently, so i also asked here.
Kindly please understand I only started to use Autofixture 2 days ago. So if there's something that I write wrong or there's a misconception in any Design Patterns, please kindly wrote a comment so that I can take it as a lesson.
Update 1:
So i tried to use initialized a plain fixture without any AutoMoq customization (ie. fixture = new Fixture()) and this time it throws a AutoFixture.ObjectCreationExceptionWithPath exception, complaining that it is unable to resolve DbSet property within the ApplicationDbContext. At this point, I was thinking if anyone know how to use a Relay or ISpecimenBuilder to tell Autofixture to use/call/implement all DbSet<T> properties within the ApplicationDbContext with dbContext.Set<T> because that would work if I replace all usage of DbSets in my unit tests, but as I mentioned, all IQueryable are return from DbSets so i cannot simply just replace it in ApplicationDbContext.
Update 2:
I remove and simplify the creation of ApplicationDbContext from my factory method CreateDbContext() since it will cause confusion from the code complexity.
It is hard to understand what you are trying to achieve from your post.
I think what you actually need, is to test your code that happens to use EntityFramework.
If that's the case you might want to have a look at this library, I have created EntityFrameworkCore.AutoFixture. It uses the In-Memory database provider as well as SQLite in-memory provider.
Have a look at the readme for some code examples. If you have any questions drop me a message or open an issue on GitHub.

Integration tests with asp.net core (test of controllers without the views)

I am trying to setup a test project to test my controllers with identity and the database without having to define the views.
I have a unit test project where I can test my controller by instanciating it, passing the dbContext to the constructor.
public class EventControllerTests
{
private readonly IEventRepository _eventRepository;
private readonly EventController _controller;
private readonly AppDbContext dbContext;
const string cn = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=EventDb;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
public EventControllerTests()
{
var options = new DbContextOptionsBuilder<EVNTS.Web.Database.AppDbContext>()
.UseSqlServer(cn).Options;
dbContext = new EVNTS.Web.Database.AppDbContext(options);
// Arrange
_eventRepository = new EventRepository(dbContext);
_controller = new EVNTS.Web.Controllers.EventController(_eventRepository);
}
[Fact]
public void ActionIndexTest()
{
// Act
var result = _controller.Index(1);
// Assert
var model = (Event)result.Model;
Assert.Equal(1, model.Id);
}
}
I have an integration test project where I use a WebApplicationFactory
public class BasicTests : IClassFixture<WebApplicationFactory<EVNTS.Startup>>
{
private readonly WebApplicationFactory<EVNTS.Startup> _factory;
private readonly HttpClient _client;
public BasicTests(WebApplicationFactory<EVNTS.Startup> factory)
{
_factory = factory;
_client = _factory.CreateClient();
}
[Theory]
[InlineData("/")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Act
var response = await _client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
[Fact]
public async Task TestUserRegistration()
{
var s = _factory.Services.GetRequiredService<EVNTS.Web.Repositories.IEventRepository>();
var url = "/user/register";
var inputModel = new EVNTS.Web.ViewModels.RegisterModel()
{
UserName = "eric",
Password = "123456",
ConfirmPassword = "123456"
};
var sObj = JsonSerializer.Serialize(inputModel);
var content = new StringContent(sObj, Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _client.PostAsync(url, content);
var result = response.Content.ReadAsStringAsync();
}
}
The problem is that with the second option, the views have to be created and I need to use a library like AngleSharp to test the results.
I would like something in between where I can call the contructor directly and test the result view but with the DI injecting the UserManager and the dbContext for me.
any ideas?
Cheers
Here is the controller:
public class UserController : Controller
{
private readonly UserManager<User> _userManager;
public UserController(UserManager<User> userManager)
{
_userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> Register([FromBody] RegisterModel model)
{
IdentityResult? result=null;
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user == null)
{
user = new User
{
Id = Guid.NewGuid(),
UserName = model.UserName,
};
result = await _userManager.CreateAsync(user, model.Password);
}
}
return View(result);
}
}
I also find this usefull sometimes when you want to check the result of a controller in an integration test condition without checking the view.
You can use the dependency injection and create a scope from the WebApplicationFactory.
using (var serviceScope = Factory.Services.CreateScope())
{
var sut= serviceScope.ServiceProvider.GetService<YourController>();
}
To make this work you have to call the method AddControllersAsServices() in Startup.cs to register the controller in the DI container
services.AddControllersWithViews(options => { options.ConfigureMvcOptionsForPortalModule(); })
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddControllersAsServices();//add controller in DI to access it in integration testing
There is no in between. The first example is a unit test, while the second is an integration test. If you want to just look at the result object of the action, then you'd use the unit test methodology, and you'd need to mock out your dependencies. Otherwise, you'd use the integration test approach, and you have to deal with the actual simulated server response.
For what it's worth here, controller actions should be integration tested, since they are inherently dependent on a number of components coming together, so you should be following the second approach, parsing the HTML response, if necessary.
I don't consider myself an authority on how to perform unit testing, but since the comment section is restricted in characters I will write my comments here.
Usually, when you find yourself in a situation where it's difficult to come up with a good unit test (I won't define "good" here) more often than not, it is because there are some problems with the project structure/code design, and not actual limitations of the unit testing itself (again, not that unit testing doesn't have it's limitations, but I think this is not the case here).
Based on the above I asked you to include the action's code so we can examine what exactly are you trying to test and why it is so hard.
Here comes the heavily opinion-based part of my comment, but I leave it up to you wether you would want to take some of this or leave it.
It's not a rule, but a good rule of thumb is that the controller should contain very little business logic, which means that unit testing a controller should be basically testing the different paths that the request could go, once it hits the controller.
Generally you would want something like this:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await _userManager.FindByNameAsync(model.UserName);
...
return View(result);
which then you can unit test with something like this:
public async Task Register_Returns_BadRequest_On_Invalid_Model()
{
var testUsername = "TestUsername";
var mockUserManager = new Mock<IUserManager>();
mockUserManager.Setup(m => m.FindByNameAsync(testUsername))
.Returns(Task.FromResult(**Not sure about this part**))
var controller = new RegisterController(mockUserManager.Object);
var result = await controller.Register(model: null);
var actionResult = Assert.IsType<ActionResult<IdentityResult>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
For the happy path you want only to check that on a valid ModelState the result is of type ActionResult>
What is my idea:
When you unit test the controller you should not be bothered by the actual data, this is responsibility of other parts of the application
The controller unit test should be plain simple, most of the time you should be testing only those two cases - invalid data returns some sort of BadRequest, valid data returns the expected response
If you find yourself mocking too much objects most of the time it's a clear sign that you need some additional layer of abstraction.
In your case, in order to make my code better structured and easier for testing I would do the following:
First test for invalid ModelState - you don't want to proceed if the ModelState is invalid and this should also be covered by an unit test.
Managers should be a higher level of abstraction. Methods like FindByNameAsync and CreateAsync are more suitable for the data access layer. In the case of this action, your UserManager can have a method like Register so your controller's action look like this:
if (!ModelState.IsValid)
{
return BadRequest()
}
var result = _userManager.Register(model.UserName);
return View(result);
Now you can remove the Find and Create methods from the controller and create a UserRepository where I thin those methods belong and where you can test them in isolation.
In this setup You have these abstractions Controller -> Manager -> Repository. Now you try to test the three of these in one single method, which is causing the problems in my opinion.
Also, just because I find this a bit more tidy, usually you use a Service layer and if the structure is too complex you add the manager layer so it becomes Controller -> Manager -> Service -> Repository. In your case I'm not sure that you need this complexity so maybe just for the sake of better naming, rename the UserManager to UserService, so that your code flow is Controller -> Service -> Repository.
Also, last pease of advice. Controller testing has always been contraversial so don't be too bothered if you don't cover your controller with unit tests as much as other parts of the code. This is somewhat expected, what I wanted to tell with this post is mainly that the problem wasn't how to test but rather is the code testable as it is, which in my opinion could be improved as I've shown above. Yes, my proposition is not perfect as well but it creates smaller chunks of code which are encapsulated, don't have that many dependencies which ultimately makes them easier to test. And of course this is not a substitution of the integrations tests.
Hope this gave you some food for thought.

Unit Testing Controller with Authorization ASP.NET Core 2.0 Web API

I have a controller:
public class InvitationsController: Controller {
private readonly IMapper _mapper;
private readonly IInvitationManager _invitationManager;
private readonly UserManager<MyAppUser> _userManager;
public InvitationsController(
IInvitationManager invitationManager,
IMapper mapper,
UserManager<MyAppUser> userManager,
IJobManager jobManager
) {
_invitationManager = invitationManager;
_mapper = mapper;
_userManager = userManager;
}
[Authorization]
GetInvitationByCode(string code) { ... }
I'm trying to write unit tests using Xunit and Moq. Here is the implentation of my test:
public class InvitationsControllerTests {
private Mock<IInvitationManager> invitationManagerMock;
private Mock<UserManager<MyAppUser>> userManagerMock;
private Mock<IMapper> mapperMock;
private InvitationsController controller;
public InvitationsControllerTests() {
invitationManagerMock = new Mock<IInvitationManager>();
userManagerMock = new Mock<UserManager<MyAppUser>>();
mapperMock = new Mock<IMapper>();
controller = new InvitationsController(invitationManagerMock.Object,
mapperMock.Object,
userManagerMock.Object);
}
[Fact]
public async Task GetInvitationByCode_ReturnsInvitation() {
var mockInvitation = new Invitation {
StoreId = 1,
InviteCode = "123abc",
};
invitationManagerMock.Setup(repo =>
repo.GetInvitationByCodeAsync("123abc"))
.Returns(Task.FromResult(mockInvitation));
var result = await controller.GetInvitationByCode("123abc");
Assert.Equal(mockInvitation, result);
}
I don't think I'm using the mocking functionality correctly. Specifically with UserManager. I can't find a clear answer on using Moq to test controllers protected by [Authorize]. When running my tests, it throws an exception on
controller = new InvitationsController(invitationManagerMock.Object,
mapperMock.Object,
userManagerMock.Object);
Which reads:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: 'Can not instantiate proxy of class: Microsoft.AspNetCore.Identity.UserManager`1[[MyApp.api.Core.Models.MyAppUser, MyApp.api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].
Could not find a parameterless constructor.'
You're not unit testing; you're integration testing. When you find yourself setting up ten thousand mocks just to run a method, that's a pretty good sign it's an integration test. Additionally, things like authorization only happen as part of the request lifecycle; there's no way to test that, without doing an actual request, which again, means you're integration testing.
As such, use the test host.
private readonly TestServer _server;
private readonly HttpClient _client;
public MyTestClass()
{
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
}
[Fact]
public async Task GetInvitationByCode_ReturnsInvitation() {
var mockInvitation = new Invitation {
StoreId = 1,
InviteCode = "123abc",
};
var response = await _client.GetAsync("/route");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Invitation>(responseString);
// Compare individual properties you care about.
// Comparing the full objects will fail because of reference inequality
Assert.Equal(mockInvitation.StoreId, result.StoreId);
}
If you need to scaffold your data to make the correct result return, simply use the in-memory database provider. The easiest way to use this for integration testing is to specify a new environment like "Test". Then, in your startup, when configuring your context, branch on the environment and use the in-memory provider (instead of SQL Server or whatever) when the environment is "Test". Then, when setting up your test server for integration testing, simply add .UseEnvironment("Test") before .UseStartup<Startup>().
I think, problem is in dependency injection. In your Startups.cs file you could find similar string: services.AddIdentity<AppUser, AppRole>().AddEntityFrameworkStores<AppDbContext>().AddDefaultTokenProviders(); it means that magic of namespace Microsoft.Extensions.DependencyInjection provide you an instance of your User- or RoleManger anywhere where you want to use it. For example, in InvitationsController using injectin in constructor.
You can try inject UserManger in test class and mock it. Or read similar question

Unit Testing a controller that uses windows authentication

-------Please see updates below as I now have this set up for dependency injection and the use of the MOQ mocking framework. I'd still like to split up my repository so it doesn't directly depend on pulling the windowsUser within the same function.
I have a Web API in an intranet site that populates a dropdown. The query behind the dropdown takes the windows username as a parameter to return the list.
I realize I don't have all of this set up correctly because I'm not able to unit test it. I need to know how this "should" be set up to allow unit testing and then what the unit tests should look like.
Additional info: this is an ASP.NET MVC 5 application.
INTERFACE
public interface ITestRepository
{
HttpResponseMessage DropDownList();
}
REPOSITORY
public class ExampleRepository : IExampleRepository
{
//Accessing the data through Entity Framework
private MyDatabaseEntities db = new MyDatabaseEntities();
public HttpResponseMessage DropDownList()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
//Pass the parameter to a procedure running a select query
var sourceQuery = (from p in db.spDropDownList(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
public ExampleController()
{
_exampleRepository = new ExampleRepository();
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 1
I have updated my Controller based on BartoszKP's suggestion to show dependency injection.
UPDATED CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
//Dependency Injection
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 2
I have decided to use MOQ as a mocking framework for unit testing. I'm able to test something simple, like the following. This would test a simple method that doesn't take any parameters and doesn't include the windowsUser part.
[TestMethod]
public void ExampleOfAnotherTest()
{
//Arrange
var mockRepository = new Mock<IExampleRepository>();
mockRepository
.Setup(x => x.DropDownList())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
ExampleController controller = new ExampleController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.DropDownList();
//Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
I need help testing the DropDownList method (one that does include code to get the windowsUser). I need advice on how to break this method apart. I know both parts shouldn't been in the same method. I don't know how to arrange splitting out the windowsUser variable. I realize this really should be brought in as a parameter, but I can't figure out how.
You usually do not unit-test repositories (integration tests verify if they really persist the data in the database correctly) - see for example this article on MSDN:
Typically, it is difficult to unit test the repositories themselves, so it is often better to write integration tests for them.
So, let's focus on testing only the controller.
Change the controller to take IExampleRepository in its constructor as a parameter:
private IExampleRepository _exampleRepository;
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
Then, in your unit tests, use one of mocking frameworks (such as RhinoMock for example) to create a stub for the sole purpose of testing the controller.
[TestFixture]
public class ExampleTestFixture
{
private IExampleRepository CreateRepositoryStub(fake data)
{
var exampleRepositoryStub = ...; // create the stub with a mocking framework
// make the stub return given fake data
return exampleRepositoryStub;
}
[Test]
public void GivenX_WhenDropDownListIsRequested_ReturnsY()
{
// Arrange
var exampleRepositoryStub = CreateRepositoryStub(X);
var exampleController = new ExampleController(exampleRepositoryStub);
// Act
var result = exampleController.DropDownList();
// Assert
Assert.That(result, Is.Equal(Y));
}
}
This is just a quick&dirty example - CreateRepositoryStub method should be of course extracted to some test utility class. Perhaps it should return a fluent interface to make the test's Arrange section more readable on what is given. Something more like:
// Arrange
var exampleController
= GivenAController()
.WithFakeData(X);
(with better names that reflect your business logic of course).
In case of ASP.NET MVC, the framework needs to know how to construct the controller. Fortunately, ASP.NET supports the Dependency Injection paradigm and a parameterless constructor is not required when using MVC unity.
Also, note the comment by Richard Szalay:
You shouldn't use HttpContext.Current in WebApi - you can use base.User which comes from HttpRequestBase.User and is mockable. If you really want to continue using HttpContext.Current, take a look at Mock HttpContext.Current in Test Init Method
One trick that I find very useful when trying to make old code testable when said code is accessing some global static or other messy stuff that I can't easily just parameterize is to wrap access to the resource in a virtual method call. Then you can subclass your system under test and use that in the unit test instead.
Example, using a hard dependency in the System.Random class
public class Untestable
{
public int CalculateSomethingRandom()
{
return new Random().Next() + new Random().Next();
}
}
Now we replace var rng = new Random();
public class Untestable
{
public int CalculateSomethingRandom()
{
return GetRandomNumber() + GetRandomNumber();
}
protected virtual int GetRandomNumber()
{
return new Random().Next();
}
}
Now we can create a testable version of the class:
public class Testable : Untestable
{
protected override int GetRandomNumber()
{
// You can return whatever you want for your test here,
// it depends on what type of behaviour you are faking.
// You can easily inject values here via a constructor or
// some public field in the subclass. You can also add
// counters for times method was called, save the args etc.
return 4;
}
}
The drawback with this method is that you can't use (most) isolation frameworks to implement protected methods (easily), and for good reason, since protected methods are sort of internal and shouldn't be all that important to your unit tests. It's still a really handy way of getting things covered with tests so you can refactor them, instead of having to spend 10 hours without tests, trying to do major architectual changes to your code before you get to "safety".
Just another tool to keep in mind, I find it comes in handy from time to time!
EDIT: More concretely, in your case you might want to create a protected virtual string GetLoggedInUserName(). This will technically speaking keep the actual call to HttpContext.Current.User.Identity.Name untested, but you will have isolated it to the simplest smallest possible method, so you can test that the code is calling the correct method the right amount of times with the correct args, and then you simply have to know that HttpContext.Current.User.Identity.Name contains what you want. This can later be refactored into some sort of user manager or logged in user provider, you'll see what suits best as you go along.

Is it possible to unit test methods that rely on NHibernate Detached Criteria?

I have tried to use Moq to unit test a method on a repository that uses the DetachedCriteria class. But I come up against a problem whereby I cannot actually mock the internal Criteria object that is built inside. Is there any way to mock detached criteria?
Test Method
[Test]
[Category("UnitTest")]
public void FindByNameSuccessTest()
{
//Mock hibernate here
var sessionMock = new Mock<ISession>();
var sessionManager = new Mock<ISessionManager>();
var queryMock = new Mock<IQuery>();
var criteria = new Mock<ICriteria>();
var sessionIMock = new Mock<NHibernate.Engine.ISessionImplementor>();
var expectedRestriction = new Restriction {Id = 1, Name="Test"};
//Set up expected returns
sessionManager.Setup(m => m.OpenSession()).Returns(sessionMock.Object);
sessionMock.Setup(x => x.GetSessionImplementation()).Returns(sessionIMock.Object);
queryMock.Setup(x => x.UniqueResult<SopRestriction>()).Returns(expectedRestriction);
criteria.Setup(x => x.UniqueResult()).Returns(expectedRestriction);
//Build repository
var rep = new TestRepository(sessionManager.Object);
//Call repostitory here to get list
var returnR = rep.FindByName("Test");
Assert.That(returnR.Id == expectedRestriction.Id);
}
Repository Class
public class TestRepository
{
protected readonly ISessionManager SessionManager;
public virtual ISession Session
{
get { return SessionManager.OpenSession(); }
}
public TestRepository(ISessionManager sessionManager)
{
}
public SopRestriction FindByName(string name)
{
var criteria = DetachedCriteria.For<Restriction>().Add<Restriction>(x => x.Name == name)
return criteria.GetExecutableCriteria(Session).UniqueResult<T>();
}
}
Note I am using "NHibernate.LambdaExtensions" and "Castle.Facilities.NHibernateIntegration" here as well. Any help would be gratefully appreciated.
Essentially I am getting a null reference exception on the assert of the object returned. Thus I assume that I have not connected up the criteria correctly. But I don't think I can do this because the criteria is a private field of the Detached Criteria which is created inside my repository!
Honestly I gave up on trying to unit test anything that touches the database a long time ago.
It's so much easier to spin up an in memory Sqlite db and just run the actual tests. Or if you would rather run them against the real database then just move them into your integration tests that only get ran when you do a checkin to source control.
I think you're missing the point of using mocking in this situtation. What you want to mock is the method
public SopRestriction FindByName(string name)
{
...
}
So then you can return any type of SopRestriction you want and not worry about the fact it's querying NHibernate.
It's pointless to ever mock any type of datacontext because you'll never gain any value.
The easiest way to do this would be to extract an interface from TestRepository so like ITestRepository and then make the rest of your dependency graph dependent on ITestRepository and you can mock the repository itself easily in your unit tests.
Follow up: Regarding your response about wanting to verify method calls inside your repository what I would reccomend is wrapping all of the NHibernate specific usage into methods themselves that don't have any type of parameter or return that is NHibernate specific so then you can mock those methods and just expect them to work. This is why unit testing is less valuable at this stage because you don't gain much. With what you said I wouldn't mock them at all but would make them full "integration" tests that touch the database or do what they need to do. I still consider these to be unit tests even if TDD purists would say they're integration tests.

Categories