I am attempting to mock out my SignalR hub for some unit tests. I am running into an issue on my Hubs OnConnectedAsync() call due to using a header to auto join a group if it exists. My issue is lying with the fact that the HubCallerContext.GetHttpContext() method is an extension method and cant be mocked. I dunno if there is a work around to this and I cant seem to find any similarly posted question about this.
OnConnectedAsync() Segment
Context.GetHttpContext().Request.Headers.TryGetValue(SignalRHeaders.GroupHeader, out StringValues header);
if (header.Any())
{
await Groups.AddToGroupAsync(Context.ConnectionId, SignalRConstants.Group);
}
Base Test Class
public DefaultHubBaseTest()
{
var memberId = Guid.NewGuid().ToString();
var orgId = Guid.NewGuid().ToString();
MockClients = new Mock<IHubCallerClients>();
MockClientProxy = new Mock<IClientProxy>();
MockClients.Setup(clients => clients.Group(It.IsAny<string>()))
.Returns(MockClientProxy.Object);
MockGroups = new Mock<IGroupManager>();
MockGroups.Setup(x => x.AddToGroupAsync(It.IsAny<string>(), It.IsAny<string>(), default(CancellationToken))).Returns(Task.CompletedTask);
MockGroups.Setup(x => x.RemoveFromGroupAsync(It.IsAny<string>(), It.IsAny<string>(), default(CancellationToken))).Returns(Task.CompletedTask);
Mock<HttpRequest> MockRequest = new Mock<HttpRequest>();
MockRequest.Setup(x => x.Headers).Returns(new HeaderDictionary()
{
{ SignalRHeaders.GroupHeader, orgId },
{ SignalRHeaders.GroupAdminHeader, "t" },
});
Mock<HttpContext> MockHttpContext = new Mock<HttpContext>();
MockHttpContext.Setup(x => x.Request).Returns(MockRequest.Object);
MockContext = new Mock<HubCallerContext>();
MockContext.Setup(x => x.ConnectionId).Returns("1");
MockContext.Setup(x => x.User.Claims).Returns(new List<Claim>() { new Claim(SignalRConstants.AzureAuthOID, memberId) });
MockContext.Setup(x => x.GetHttpContext()).Returns(MockHttpContext.Object);
DefaultHub = new DefaultHub()
{
Context = MockContext.Object,
Groups = MockGroups.Object,
Clients = MockClients.Object,
};
}
If anyone could help me with this, I would greatly appreciate it. Thanks!
Related
When I'm trying to test a window.open(url, '_blank'), it'll automatically open a new tab in my browser during the testing. Is there any way I can prevent this from happening?
Like, try to open a new tab, but don't do it. I know you could do jasmine.createSpyObj('obj', ['methods']).and.returnValue({...}), but I'm not sure how to do this for window.open.
NavigationService
export class NavigationService {
navigate(url: string, openInNewTab: boolean = false): boolean {
if (url.startsWith('http')) {
openInNewTab ? window.open(url, '_blank') : ((window as any).location.href = url);
return false;
}
...
}
}
NavigationService spec
describe('NavigationService', () => {
let navigationService: NavigationService;
let routerSpy, configServiceSpy, windowSpy;
let httpUrlMock = 'http://mock.com';
beforeEach(() => {
routerSpy = jasmine.createSpyObj('Router', ['createUrlTree', 'navigateByUrl']);
configServiceSpy = jasmine.createSpyObj('ConfigService', ['current']);
windowSpy = spyOn(window, 'open').and.callThrough();
navigationService = new NavigationService(routerSpy, configServiceSpy);
});
it('should open the url in a new tab when the url starts with http ', () => {
let _result = navigationService.navigate(httpUrlMock, true);
expect(windowSpy).toHaveBeenCalledWith(httpUrlMock, '_blank');
});
}
The problem with your code is the using of callThrough method, which is responsible for opening the URL. Just remove it and it will resolve your issue.
Just use the below line:
windowSpy = spyOn(window, 'open');
Use of callthrough
I tried following code but it's give exceptions:
var mockIdsDbContext = new Mock<IdentityServerDbContext>();
var applicationUser = new ApplicationUser()
{
Id = Guid.NewGuid().ToString(),
Email = "shashikant0423#gmail.com",
PhoneNumber = "999999999"
};
var usersTestData = new List<ApplicationUser>() { applicationUser };
var users = MockDbSet(usersTestData);
mockIdsDbContext.Setup(x => x.Users).Returns(userManager.Object.Users);
Mock<DbSet<T>> MockDbSet<T>(IEnumerable<T> list) where T : class, new()
{
IQueryable<T> queryableList = list.AsQueryable();
Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
//dbSetMock.Setup(x => x.Create()).Returns(new T());
return dbSetMock;
}
but I got error like:
Can not instantiate proxy of class: RenewPlus.IdentityServer.Data.IdentityServerDbContext. Could not find a parameterless constructor
Instead of mocking the rather complex DbContext I'd recommend instead using the Microsoft.EntityFrameworkCore.InMemory driver.
https://learn.microsoft.com/en-us/ef/core/providers/in-memory/
I have below code which navigates from one page to other using Navigation Service in Xamarin forms.
On clicking on Observation button it executes ObservationsCommand as shown below.
public Command ObservationsCommand => new Command(async () => await OnObservationsCommandAsync());
After clicking on Observation button it navigates to next page passing the selected data to the navigation service as shown
private async Task OnObservationsCommandAsync()
{
ObservationDetailsParameter selectedData = new ObservationDetailsParameter
{
Cage = DisplayedCage,
Dossiers = DossierList.SelectedItems
};
await _navigationService.NavigateToAsync<ObservationDetailsViewModel>(selectedData);
}
Below is the code for unit test
[Fact]
public void TestOnObservationsCommandAsync()
{
var mockNavigationService = new Mock<INavigationService>();
var mockCageDetailsService = new MockCageDetailsService();
var mockObservationDetailsService = new MockObservationDetailsService();
var mockSettingsService = new MockSettingsService();
Cage _displayedCage = new Cage { Id = 11 };
Dossier _dossier1 = new Dossier { Id = 841 };
var _dossierList = new SelectableItemCollection<Dossier>
{
_dossier1
};
_dossierList.SelectAll();
var cageObsViewModel = new CageObsViewModel(mockNavigationService.Object, mockCageDetailsService, mockSettingsService);
var mockObservationDetailsParameter = new
Mock<IObservationDetailsParameter>();
mockObservationDetailsParameter.Setup(x =>
x.Cage).Returns(_displayedCage);
mockObservationDetailsParameter.Setup(x => x.Dossiers
).Returns(_dossierList.SelectedItems );
cageObsViewModel.DisplayedCage = mockObservationDetailsParameter .Object .Cage ;
cageObsViewModel.DossierList = _dossierList;
// Act
cageObsViewModel.ObservationsCommand.Execute(null);
mockNavigationService.Verify((s) => s.NavigateToAsync<ObservationDetailsViewModel>(mockObservationDetailsParameter.Object));
}
However after executing this test i get error as 'Expected invocation on the mock at least once, but was never performed.'
Will you please help?
This is an example of a unit test I have to verify navigation after successful login.
[Fact]
public async Task ValidCredentials()
{
//Mock login -> false
var authenticationServiceMock = new Mock<IAuthenticationService>();
authenticationServiceMock
.Setup(s => s.Login(It.IsAny<string>(), It.IsAny<string>()))
.Returns(Task.FromResult(true));
LoginViewModel viewModel = new LoginViewModel(authenticationServiceMock.Object);
await viewModel.InitializeAsync(null);
viewModel.UserName.Value = "johann#mail.com";
viewModel.Password.Value = "1234!Maaaa";
viewModel.LoginCommand.Execute(null);
navigationServiceMock.Verify((s) => s.NavigateToAsync<HomeViewModel>());
}
As I am using AutoFac and my ViewModelBase gets the NavigationService instance by itself this is my test setup:
public LoginViewModelTests()
{
ContainerBuilder builder = new ContainerBuilder();
navigationServiceMock = new Mock<INavigationService>();
navigationServiceMock.SetReturnsDefault<Task>(Task.FromResult(0));
builder.RegisterInstance<INavigationService>(navigationServiceMock.Object);
ViewModelLocator.RegisterDependencies(builder);
}
If it is not clear enough let me know and we can review it to better adapt to your case.
How to mock Url.Action during testing controller action?
I'm trying to unit test my asp.net core controller action.
Logic of action has Url.Action and I need to mock it to complete test but I can't find right solution.
Thank you for your help!
UPDATE
this is my method in controller that I need to test.
public async Task<IActionResult> Index(EmailConfirmationViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user == null) return RedirectToAction("UserNotFound");
if (await _userManager.IsEmailConfirmedAsync(user)) return RedirectToAction("IsAlreadyConfirmed");
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("Confirm", "EmailConfirmation", new { userId = user.Id, token }, HttpContext.Request.Scheme);
await _emailService.SendEmailConfirmationTokenAsync(user, callbackUrl);
return RedirectToAction("EmailSent");
}
return View(model);
}
I have problem with mocking this part:
var callbackUrl = Url.Action("Confirm", "EmailConfirmation", new { userId = user.Id, token }, HttpContext.Request.Scheme);
Finally I found solution!
When you are mocking UrlHelper you need to mock only base method Url.Action(UrlActionContext context) because all helper methods actually use it.
var mockUrlHelper = new Mock<IUrlHelper>(MockBehavior.Strict);
mockUrlHelper
.Setup(
x => x.Action(
It.IsAny<UrlActionContext>()
)
)
.Returns("callbackUrl")
.Verifiable();
_controller.Url = mockUrlHelper.Object;
Also! I have problem because of null in HttpContext.Request.Scheme. You need to mock HttpContext
_controller.ControllerContext.HttpContext = new DefaultHttpContext();
I added
var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
.Setup(x => x.Action(It.IsAny<UrlActionContext>()))
.Returns((UrlActionContext uac) =>
$"{uac.Controller}/{uac.Action}#{uac.Fragment}?"
+ string.Join("&", new RouteValueDictionary(uac.Values).Select(p => p.Key + "=" + p.Value)));
controller.Url = urlHelperMock.Object;
To my generic Controller setup. Which is a bit roughnready but means I can test any controller logic that generates links.
I am trying to test a repository and so need to mock the Model Container. Really all I need is to be able to set the Blogs returned to GetBlogs() in the BlogRepository. The repository code is:
private BlogWebsiteModelContainer context;
public BlogRepository(BlogWebsiteModelContainer context)
{
this.context = context;
}
public IEnumerable<Blog> GetBlogs()
{
return context.Blogs;
}
So I want to be able to set what context.Blogs is. I am using Moq and have tried the following:
var mockBlogSet = new Mock<DbSet<Blog>>();
context.Setup(m => m.Blogs).Returns(mockBlogSet.Object);
blogRepo = new BlogRepository(context.Object);
But I get this error message when I debug:
Invalid setup on a non-virtual (overridable in VB) member: m => m.Blogs
Any help greatly appreciated.
Create an interface for your BlogWebsiteModelContainer, then mock the interface. Also instead of defining Blogs on the interface as DbSet<Blog> define it as IQuerayable<Blog>
You can then create a List and use the .AsQueryable extension:
var contextMock = new Mock<IBlogWebsetModelContainer>();
var mockBlogSet = new List<Blog>();
contextMock.Setup(m => m.Blogs).Returns(mockBlogSet.AsQueryable());
blogRepo = new BlogRepository(contextMock.Object);
I found the answer at this link http://msdn.microsoft.com/en-us/data/dn314429.aspx. And so my code became the following:
List<Blog> blogs = new List<Blog>
{
new Blog() { Id = 1 },
new Blog() { Id = 2 },
new Blog() { Id = 3 },
};
var data = blogs.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<BlogWebsiteModelContainer>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
blogRepo = new BlogRepository(mockContext.Object);
Then I had to go into my BlogWebsiteModelContainer class and change:
public DbSet<Blog> Blogs { get; set; }
to
public virtual DbSet<Blog> Blogs { get; set; }
Just adding the virtual in. And it all worked.
Quick note: reason for creating the list first and then making it .AsQueryable() was so in my code I had the original blog list separate so I could compare it in my tests.