ASP.NET Generic method to mock EF6 DbSet - c#

In my ASP.NET MVC5 web-app I'm using the EF (Model-first) for my database-communication. For unit-testing purposes, I created a generic method to generate Mock database sets. Unfortunately, I am unable to mock all the methods because when .Where(), .Any() and .Find() are used in my tested code, an exception is thrown. Can anyone help me on this? My code is below.
Unittest:
[TestClass()]
public class MessagingServiceTests
{
Mock<BoatstersEntitiesContainer> _mockContext;
MessagingService _service;
string _connectionId;
Guid _userId;
[TestInitialize()]
public void TestInitialize()
{
_userId = Guid.Parse("12345678-1234-1234-1234-123412344142");
_connectionId = "abc123";
// Setup entities
User user = new User { Id = _userId, CustomerId = 1 };
Customer customer = new Customer { Id = 1, User = user, FirstName = "TestFirstName" };
user.Customer = customer;
Customer boatOwnerCustomer = new Customer { Id = 2, FirstName = "BoatOwner" };
Boat boat = new Boat { Id = 1, Customer = boatOwnerCustomer, CustomerId = boatOwnerCustomer.Id };
boatOwnerCustomer.Boats.Add(boat);
// Init mocksets
var userMockSet = MockDbSet.Build(new List<User> { user });
var customerMockSet = MockDbSet.Build(new List<Customer> { customer, boatOwnerCustomer });
var conversationMockSet = MockDbSet.Build(new List<Conversation>());
var messageMockSet = MockDbSet.Build(new List<Message>());
var boatMockSet = MockDbSet.Build(new List<Boat> { boat });
var messagingHubConnectionMockSet = MockDbSet.Build(new List<MessagingHubConnection>());
// Setup mockcontext
_mockContext = new Mock<BoatstersEntitiesContainer>();
_mockContext.Setup(m => m.Users).Returns(userMockSet.Object);
_mockContext.Setup(m => m.Customers).Returns(customerMockSet.Object);
_mockContext.Setup(m => m.Conversations).Returns(conversationMockSet.Object);
_mockContext.Setup(m => m.Messages).Returns(messageMockSet.Object);
_mockContext.Setup(m => m.Boats).Returns(boatMockSet.Object);
_mockContext.Setup(m => m.MessagingHubConnections).Returns(messagingHubConnectionMockSet.Object);
// Start service
_service = new MessagingService(_mockContext.Object, _userId);
}
[TestMethod()]
public void When_PartnerConnected_IsTrue()
{
Conversation conversation = new Conversation {
Id = 1,
Boat = _mockContext.Object.Boats.First(b => b.Id.Equals(1)),
BoatId = _mockContext.Object.Boats.First(b => b.Id.Equals(1)).Id
};
conversation.Customers.Add(_mockContext.Object.Customers.First(b => b.Id.Equals(1)));
conversation.Customers.Add(_mockContext.Object.Customers.First(b => b.Id.Equals(2)));
MessagingHubConnection connection = new MessagingHubConnection
{
Id = 1,
Connected = true,
ConnectionId = "abc123",
Conversation = conversation,
ConversationId = 1,
Customer = _mockContext.Object.Customers.First(b => b.Id.Equals(2)),
CustomerId = 2
};
conversation.MessagingHubConnections.Add(connection);
_mockContext.Object.MessagingHubConnections.Add(connection);
_mockContext.Object.Conversations.Add(conversation);
var result = _service.IsPartnerConnected();
Assert.IsTrue(result);
// Clean up
_mockContext.Object.Conversations.RemoveRange(_mockContext.Object.Conversations);
_mockContext.Object.MessagingHubConnections.RemoveRange(_mockContext.Object.MessagingHubConnections);
}
}
Generic mockset creator:
public static class MockDbSet
{
public static Mock<DbSet<TEntity>> Build<TEntity>(List<TEntity> data) where TEntity : class
{
var queryable = data.AsQueryable();
var mockSet = new Mock<DbSet<TEntity>>();
mockSet.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockSet.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockSet.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockSet.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
mockSet.Setup(m => m.Add(It.IsAny<TEntity>())).Callback<TEntity>(data.Add);
return mockSet;
}
}
Messagingservice (which is being tested)
public class MessagingService : BaseRepository<Conversation>
{
private readonly Customer _customer;
private MessagingHubConnection _connection;
public MessagingService(BoatstersEntitiesContainer context, Guid userId) : base(context)
{
Context = context;
_customer = Context.Customers.First(c => c.User.Id == userId);
}
public bool IsPartnerConnected()
{
// Check if partner is connected
return Context.MessagingHubConnections.Any(c => c.ConversationId.Equals(_connection.ConversationId) && c.Customer.Id != _customer.Id && c.Connected);
}
}
In MessagingService.IsPartnerConnected(), the following exception is thrown:
Test Name: When_PartnerConnected_IsTrue
Test FullName: Boatsters.Sunshine.UnitTests.MessagingServiceTests.When_PartnerConnected_IsTrue
Test Source: C:\Users\Jelle\Source\Repos\Boatsters.Sunshine\Boatsters.Sunshine.UnitTests\MessagingServiceUnitTest.cs : line 94
Test Outcome: Failed
Test Duration: 0:00:00,0289022
Result StackTrace:
bij lambda_method(Closure , MessagingHubConnection )
bij System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 predicate)
bij lambda_method(Closure )
bij System.Linq.EnumerableExecutor1.Execute()
bij System.Linq.EnumerableQuery1.System.Linq.IQueryProvider.Execute[S](Expression expression)
bij System.Linq.Queryable.Any[TSource](IQueryable1 source, Expression1 predicate)
bij Boatsters.Services.MessagingService.IsPartnerConnected() in C:\Users\Jelle\Source\Repos\Boatsters.Sunshine\Boatsters.Services\MessagingService.cs:line 156
bij xxx.MessagingServiceTests.When_PartnerConnected_IsTrue() in C:\xxx\MessagingServiceUnitTest.cs:line 118
Result Message:
Test method
xxx.UnitTests.MessagingServiceTests.When_PartnerConnected_IsTrue threw exception:
System.NullReferenceException: Object reference not set to an instance of an object

Based on review of MessagingService, it is not immediately apparent where _connection is assigned a value. Looks like that is what is null when the method under test is called based on stack trace lambda_method(Closure , MessagingHubConnection )
Also from past experiences with Moq and DbSet<>, this line needs to updated so that multiple calls can be made to the data source.
mockSet.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
Change the .Returns(queryable.GetEnumerator()) to return a Func
mockSet.As<IQueryable<TEntity>>()
.Setup(m => m.GetEnumerator())
.Returns(() => queryable.GetEnumerator()); //<-- Note change here.
The original will return the same enumerator for every call which can only be enumerated once and may cause issues. Using the Func will allow a fresh enumerator to be return on every call to allow for multiple passes on the data source.

Related

How to solve IQueryable' doesn't implement 'IAsyncQueryProvider issue

i'm trying to write a test to test that the method that gets categoryById returns the correct data but i'm getting a System.AggregateException. please does anyone have an idea how to fix this issue? thanks
Test setup:
[SetUp]
public void Setup()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MapperInitilizer>();
});
_realMapper = new Mapper(config);
_categories = new Mock<IRepository<Category>>();
_unitOfWork = new Mock<IUnitOfWork>();
_unitOfWork.SetupGet(work => work.Categories).Returns(_categories.Object);
_logger = new Mock<ILogger<CategoryController>>();
_mapper = new Mock<IMapper>();
_categoryController = new CategoryController(_logger.Object, _mapper.Object,_unitOfWork.Object);
category_one = new Category()
{
Id = 1,
CreatedAt = new DateTime(2022, 8,8),
Description = "music desc",
Title = "music"
};
category_two = new Category()
{
Id = 2,
CreatedAt = new DateTime(2022, 9,8),
Description = "sport desc",
Title = "sport"
};
}
CategoryTestRepository's Get method
public async Task<Category> Get(Expression<Func<Category, bool>> expression, Func<IQueryable<Category>, IIncludableQueryable<Category, object>> include = null)
{
IQueryable<Category> query = Source.AsQueryable();
return await query.AsNoTracking().FirstOrDefaultAsync(expression);
}
Get categoryById test
[Test]
public void GetCategory_InputCategoryId_ReturnCategoryById()
{
List<Category> categoryList = new List<Category> {category_one, category_two};
var expectedResult = categoryList[0];
var repo = new CategoryTestRepository(categoryList);
var unitOfWork = new Mock<IUnitOfWork>();
unitOfWork.SetupGet(work => work.Categories).Returns(repo);
var categoryController = new CategoryController(_logger.Object, _realMapper, unitOfWork.Object);
categoryController.GetCategory(1).GetAwaiter().GetResult();
Assert.IsNotEmpty(repo.Source);
Assert.AreEqual(expectedResult.Id, repo.Get(x => x.Id == 1).Result);
}
Error:
System.AggregateException : One or more errors occurred. (The provider for the source
'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only provi...
System.AggregateException : One or more errors occurred. (The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.)
----> System.InvalidOperationException : The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()

Mocking IStringLocalizerFactory in C# throws NullReferenceException

I'm trying to write a test for a service that uses IStringLocalizerFactory to translate strings. All of the translations are in a single Resource file. I cannot seem to get the Mock for it to work, as it always throws a NullReferenceException. When debugging, it shows that _localizer is null. When I remove the localizer logic completely, the test succeeds.
Code I'm trying to test:
private readonly IStringLocalizer _localizer;
public EventService(IEventRepository eventRepository, IMemberEventRepository memberEventRepository, IStringLocalizerFactory factory)
{
this._eventRepository = eventRepository;
this._memberEventRepository = memberEventRepository;
this._localizer = factory.Create(typeof(Resource));
}
public async Task CreateEventRegistrationAsync(MemberEvent entity)
{
if (await this._memberEventRepository.GetMemberEvent(entity.EventId, entity.MemberId) != null)
{
throw new ArgumentException(_localizer["This member already participates in this event."].Value);
}
await this._memberEventRepository.CreateAsync(entity);
}
My tests:
private Mock<IStringLocalizerFactory> _stringLocalizerFactoryMock = new Mock<IStringLocalizerFactory>();
public EventServiceTests()
{
_service = new EventService(_eventRepoMock.Object, _memberEventRepoMock.Object, _stringLocalizerFactoryMock.Object);
}
[Fact]
public async Task CreateEventRegistrationAsync_ShouldThrowArgumentException_WhenMemberAlreadyRegisteredForEvent()
{
int eventId = 456;
int memberId = 123;
_stringLocalizerFactoryMock.Setup(x => x.Create(typeof(Resource)))
.Returns(() => new StringLocalizer<Resource>(_stringLocalizerFactoryMock.Object));
MemberEvent registration = new MemberEvent
{
EventId = eventId,
MemberId = memberId
};
_memberEventRepoMock.Setup(x => x.GetMemberEvent(eventId, memberId))
.ReturnsAsync(registration);
await Assert.ThrowsAsync<ArgumentException>(async () => await _service.CreateEventRegistrationAsync(registration));
}
From examining the subject under test I see that
_localizer["This member already participates in this event."].Value
will throw a null exception because _localizer[...] was not setup and thus fail when .Value is invoked.
Consider mocking a IStringLocalizer<T> so that it can be setup to behave as expected when the test is invoked.
//...
var localizer = new Mock<IStringLocalizer<Resource>>();
localizer
.Setup(_ => _[It.IsAny<string>()])
.Returns(string key => new LocalizedString(key, key));
_stringLocalizerFactoryMock
.Setup(_ => _.Create(It.IsAny<Type>()))
.Returns(() => localizer.Object));
//...

EF Core Unit Test Mocking

I am attempting to mock out some of EF Core's functions.
Originally I was getting the following error
The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations.
After looking at this question it almost seems to work but i'm now getting the following:
System.ArgumentNullException : Value cannot be null.
Parameter name: source
at Microsoft.EntityFrameworkCore.Utilities.Check.NotNull[T](T value, String parameterName)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable1 source, Expression1 predicate, CancellationToken cancellationToken)
at Ombi.Core.Rule.Rules.Search.RadarrCacheRule.d__2.MoveNext() in C:\Users\Jamie.Rees\Source\Repos\PlexRequests.Net\src\Ombi.Core\Rule\Rules\Search\RadarrCacheRule.cs:line 25
Here is the test code:
[Test]
public async Task Should_ReturnApproved_WhenMovieIsInRadarr()
{
var list = new List<RadarrCache>(){new RadarrCache
{
TheMovieDbId = 123
}}.AsQueryable();
var radarrMock = new Mock<DbSet<RadarrCache>>();
radarrMock.As<IAsyncEnumerable<RadarrCache>>()
.Setup(m => m.GetEnumerator())
.Returns(new TestAsyncEnumerator<RadarrCache>(list.GetEnumerator()));
radarrMock.As<IQueryable<RadarrCache>>()
.Setup(m => m.Provider)
.Returns(new TestAsyncQueryProvider<RadarrCache>(list.Provider));
radarrMock.As<IQueryable<RadarrCache>>().Setup(m => m.Expression).Returns(list.Expression);
radarrMock.As<IQueryable<RadarrCache>>().Setup(m => m.ElementType).Returns(list.ElementType);
radarrMock.As<IQueryable<RadarrCache>>().Setup(m => m.GetEnumerator()).Returns(() => list.GetEnumerator());
ContextMock.Setup(c => c.Set<RadarrCache>()).Returns(radarrMock.Object);
var request = new SearchMovieViewModel { Id = 123 };
var result =await Rule.Execute(request);
Assert.True(result.Success);
Assert.True(request.Approved);
}
This is the class under test:
public class RadarrCacheRule : BaseSearchRule, IRules<SearchViewModel>
{
public RadarrCacheRule(IOmbiContext ctx)
{
_ctx = ctx;
}
private readonly IOmbiContext _ctx;
public async Task<RuleResult> Execute(SearchViewModel obj)
{
if (obj.Type == RequestType.Movie)
{
// Check if it's in Radarr
var result = await _ctx.RadarrCache.FirstOrDefaultAsync(x => x.TheMovieDbId == obj.Id);
if (result != null)
{
obj.Approved =
true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something?
}
}
return Success();
}
}
Any idea how I am suppose to do this?

My service doesn't get my mocked DbContext through its constructor

I try to create a fake db context to pass it to my service to test my functions from the service. The error i get is : Object reference not set to an instance of an object. This happens because my service doesn't get the new mocked context. I know this because watched it through debugger.
this is my unit test code :
[TestMethod]
public void VerifiesIfGetPersonByNameReturnsAValidPerson()
{
//Arrange
var data = new List<Person>
{
new Person {Name = "Ana"},
new Person {Name = "Marius"},
new Person {Name = "George"},
}.AsQueryable();
var mockSet = new Mock<DbSet<Person>>();
mockSet.As<IQueryable<Person>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Person>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Person>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Person>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<PersonDbContext>();
mockContext.Setup(c => c.Persons).Returns(mockSet.Object);
var service = new PersonService(mockContext.Object);
var expected = "Marius";
//Act
var personDto = service.GetPersonByName("Marius");
var actual = personDto.Name;
//Assert
Assert.AreEqual(expected,actual);
}
And here is my service code :
public class PersonService : IPersonService
{
private PersonDbContext _db;
public PersonService(PersonDbContext db)
{
_db = db;
}
public PersonDto GetPersonByName(string name)
{
PersonDto personDto = Mapper.Map<PersonDto>(_db.Persons.Find(name));
return personDto;
}
...}
Why the service won't take the new context through its constructor ? Or what is the problem ? Why i keep getting null results when I execute the method GetPersonByName ?

How to mock HttpContext in a ShoppingCart controller/model

In our MVC4 application with Entity Framework 4.0 based on the Music Store Tutorial we are using Moq to mock the DbContext and unit test are logic. One of our methods proves difficult to test though since it makes use of HttpContext or HttpContextBase. One example method looks like this:
public static ShoppingCart GetCart(HttpContextBase context)
{
var cart = new ShoppingCart();
cart.ShoppingCartId = cart.GetCartId(context);
return cart;
}
The only property collected from HttpContextBase is the [CartSessionKey] as can be seen here:
public string GetCartId(HttpContextBase context)
{
if (context.Session[CartSessionKey] == null)
{
if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))
{
context.Session[CartSessionKey] =
context.User.Identity.Name;
}
else
{
// Generate a new random GUID using System.Guid class
Guid tempCartId = Guid.NewGuid();
// Send tempCartId back to client as a cookie
context.Session[CartSessionKey] = tempCartId.ToString();
}
}
return context.Session[CartSessionKey].ToString();
}
We have heard horror stories that HttpContext is a very complex class and that if you print it you have enough paper to circle the earth eight times.
Nevertheless we want to mock it. The question is how. The properties that we want to mock are the [CartSessionKey], and the property that come from the context as contest.User.Identity.Name.
We suspect we need to use something like this:
var mockData = new Mock<FakeContext>();
mockData.Setup(m => m.Orders).Returns(memoryOrderItems);
mockData.Setup(m => m.Carts).Returns(memoryCartItems);
Mock<HttpContextBase> mockHttpContext = new Mock<HttpContextBase>();
Mock<HttpRequestBase> mockHttpRequest = new Mock<HttpRequestBase>();
mockHttpRequest.Setup(x => x.CartSessionKey).Returns(1);
mockHttpContext.Setup(x => x.Request).Returns(mockHttpRequest.Object);
but we cannot find how to specifically implement this so we do not get any errors on methods that use context.Session[CartSessionKey] or context.User.Identity.Name.
We hope someone can help us out.
/edit
When we do this:
var memoryUserItems = new FakeDbSet<User>()
{
new User { Email = "test#test.de",
FullName = "Test Person",
isAvailable = true,
Name = "WHat"
},
new User { Email = "test2#test.de",
FullName = "Test Person 2",
isAvailable = true,
Name = "WHat 2"
}
};
(...) Other memory...Items
And then this:
// Create mock units of work
var mockData = new Mock<FakeContext>();
mockData.Setup(m => m.Orders).Returns(memoryOrderItems);
mockData.Setup(m => m.Carts).Returns(memoryCartItems);
mockData.Setup(m => m.Users).Returns(memoryUserItems);
var principalMock = new Mock<IPrincipal>();
var identityMock = new Mock<IIdentity>();
var userMock =
identityMock.Setup(x => x.Name).Returns("Test!");
identityMock.Setup(x => x.IsAuthenticated).Returns(true); // optional ;)
mockData.Setup(x => x.Identity).Returns(identityMock.Object);
var httpReqBase = new Mock<HttpRequestBase>(); // this is useful if you want to test Ajax request checks or cookies in the controller.
var httpContextBase = new Mock<HttpContextBase>();
httpContextBase.Setup(x => x.User).Returns(principalMock.Object);
httpContextBase.Setup(x => x.Session[It.IsAny<string>()]).Returns(1); //Here is the session indexer. You can swap 'any' string for specific string.
httpContextBase.Setup(x => x.Request).Returns(httpReqBase.Object);
We get the error that:
Error 3 'project.Models.FakeContext' does
not contain a definition for 'Identity' and no extension method
'Identity' accepting a first argument of type
'project.Models.FakeContext' could be found
(are you missing a using directive or an assembly
reference?)
/ edit2
To make it more clear. The actual method I am testing is the following:
public ActionResult Complete(int id)
{
// Make sure that user is currentuser and otherwise bring user to our Thief page
if (id != db.GetCurrentUserId())
{
return View("Thief");
}
var cart = ShoppingCart.GetCart(this.HttpContext);
var currentDate = DateTime.Today;
var viewModel = new ShoppingCartViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal(),
ProductItems = db.Products.ToList()
};
if (viewModel.CartItems.Count() == 0)
{
return View("Empty");
}
// Try to write cart to order table
try
{
foreach (var item in viewModel.CartItems)
{
ProcessOrder(item, id, currentDate);
}
// after this we empty the shopping cart
cart.EmptyCart();
return View();
}
catch
{
// Invalid - display error page
return View("Error");
}
}
As can be seen the var cart = ShoppingCart.GetCart(this.HttpContext); uses this.HttpContext. In the test I just do controller.Complete(1). I cannot pass a new HttpContext to the controller I guess?
/ edit 3
While using the code below with the mocks I get the following message:
Test Name: TestCheckoutCompleteShouldWithEmptyCart
Test FullName: Controllers.CheckoutControllerTest.TestCheckoutCompleteShouldWithEmptyCart
Test Source: Controllers\CheckoutControllerTest.cs : line 141
Test Outcome: Failed
Test Duration: 0:00:00.0158591
Result Message:
Test method Controllers.CheckoutControllerTest.TestCheckoutCompleteShouldWithEmptyCart threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:
at Models\ShoppingCart.cs:line 170
at \Models\ShoppingCart.cs:line 20
at \Controllers\CheckoutController.cs:line 48
at Controllers\CheckoutControllerTest.cs:line 143
OK, here it goes. The following works in MVC5 with AD, I'm not sure if it's fully backwards compatible, you'll have to check.
var principalMock = new Mock<IPrincipal>();
var identityMock = new Mock<IIdentity>();
identityMock.Setup(x => x.Name).Returns("Test!");
identityMock.Setup(x => x.IsAuthenticated).Returns(true); // optional ;)
userMock.Setup(x => x.Identity).Returns(identityMock.Object);
var httpReqBase = new Mock<HttpRequestBase>(); // this is useful if you want to test Ajax request checks or cookies in the controller.
var httpContextBase = new Mock<HttpContextBase>();
httpContextBase.Setup(x => x.User).Returns(principalMock.Object);
httpContextBase.Setup(x => x.Session[It.IsAny<string>()]).Returns(1); //Here is the session indexer. You can swap 'any' string for specific string.
httpContextBase.Setup(x => x.Request).Returns(httpReqBase.Object);
This would help you to write a proper Unit Test using Moq.
[TestClass]
public class SutTest
{
[TestMethod]
public void GetCartId_WhenUserNameIsNotNull_SessionContainsUserName()
{
var httpContextStub = new Mock<HttpContextBase>();
var httpSessionStub = new Mock<ISessionSettings>();
httpSessionStub.Setup(x => x.Get<string>(It.IsAny<string>())).Returns(() => null);
httpSessionStub.SetupSequence(x => x.Get<string>(It.IsAny<string>()))
.Returns(null)
.Returns("FakeName");
var httpUserStub = new Mock<IPrincipal>();
var httpIdenttyStub = new Mock<IIdentity>();
httpUserStub.SetupGet(x => x.Identity).Returns(httpIdenttyStub.Object);
httpIdenttyStub.SetupGet(x => x.Name).Returns("FakeName");
httpContextStub.Setup(x => x.User).Returns(httpUserStub.Object);
var sut = new Sut(httpSessionStub.Object);
var result = sut.GetCartId(httpContextStub.Object);
Assert.AreEqual("FakeName",result );
}
}
Check the SetupSequence method which gives you find Control over different values being return on he same stubbed call.
Also important to decouple your session from HttpContext as you can always run into issues.
public class SessionSettings : ISessionSettings
{
private readonly HttpSessionStateBase _session;
public SessionSettings(HttpSessionStateBase session)
{
_session = session;
}
public T Get<T>(string key)
{
return (T)_session[key];
}
public void Set<T>(string key, T value)
{
_session[key] = value;
}
}
public interface ISessionSettings
{
T Get<T>(string key);
void Set<T>(string key, T value);
}
public class Sut
{
private ISessionSettings _sessionSettings;
public Sut(ISessionSettings sessionSettings)
{
_sessionSettings = sessionSettings;
}
public string GetCartId(HttpContextBase context)
{
if (_sessionSettings.Get<string>(CartSessionKey) == null)
{
if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))
{
_sessionSettings.Set<string>(CartSessionKey, context.User.Identity.Name);
}
else
{
// Generate a new random GUID using System.Guid class
Guid tempCartId = Guid.NewGuid();
// Send tempCartId back to client as a cookie
_sessionSettings.Set<string>(CartSessionKey, tempCartId.ToString());
}
}
return _sessionSettings.Get<string>(CartSessionKey);
}
private string CartSessionKey = "key";
}
This way the code is more readable and easier to understand.

Categories