This is my controller code and I need to mock GetTokenDetails() method to conduct XUnit test on DecodeToken function. Am I doing in right way or not?
[HttpGet]
[Authorize(Roles = "Admin")]
[Route("DecodeToken")]
public IActionResult DecodeToken()
{
if (ModelState.IsValid)
{
var tokenResult = GetTokenDetails();
var result = _employeeService.ServiceDecodeToken(tokenResult.UserName, tokenResult.Role);
if (result.Httpcode == 200)
{
return Ok(result);
}
else
{
return StatusCode(500, result);
}
}
else
return BadRequest();
}
public GetTokenDetailsDto GetTokenDetails()
{
var token = HttpContext.Request.Headers["Authorization"].ToString();
var tokenbearer = token.Split(' ');
var handler = new JwtSecurityTokenHandler();
var decodedtoken = handler.ReadJwtToken(tokenbearer[1]);
string user = decodedtoken.Claims.Where(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name").FirstOrDefault().ToString();
string role = decodedtoken.Claims.Where(x => x.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role").FirstOrDefault().ToString();
var usr = user.Split(":");
var rol = role.Split(":");
string userName = usr[2].Trim();
string userRole = rol[2].Trim();
GetTokenDetailsDto getTokenDetailsDto = new GetTokenDetailsDto()
{
UserName = userName,
Role = userRole,
};
return getTokenDetailsDto;
}
First, Generally, I don't think there is a right way to do something.
There are multiple right ways to do it :). You just need to pick one or create one.
Second, I suggest you to create a new Dotnet standard Project under the same Solution and separate the logic from controllers. This way, you can create a Unit test project and import only the logic project.
Third, I see some points in your code that you are reading some value from the context( which is not avaiable in testing environment). For example, HttpContext.Request.Headers["Authorization"]. these kind of data should be in the input arguments of the function GetTokenDetails, so you can provide some sample data to test in your UnitTestProject. Something like this:
public GetTokenDetailsDto GetTokenDetails(string token)
{
var tokenbearer = token.Split(' ');
var handler = new JwtSecurityTokenHandler();
var decodedtoken = handler.ReadJwtToken(tokenbearer[1]);
string user = decodedtoken.Claims.Where(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name").FirstOrDefault().ToString();
string role = decodedtoken.Claims.Where(x => x.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role").FirstOrDefault().ToString();
var usr = user.Split(":");
var rol = role.Split(":");
string userName = usr[2].Trim();
string userRole = rol[2].Trim();
GetTokenDetailsDto getTokenDetailsDto = new GetTokenDetailsDto()
{
UserName = userName,
Role = userRole,
};
return getTokenDetailsDto;
}
Related
Receiving an unauthorized response when trying to remove a user from a workspace (group). I'm using the .Net SDK method DeleteUserAsAdminAsync. I tried the DeleteUserInGroupAsync method with the same result. I've had no trouble retrieving groups, reports, even adding users to groups using the same authentication code shown below. I'm quite stumped.
public async Task<ResultObject> RemoveUsersFromWorkspaces(List<PowerBIWorkspace> powerBIWorkspaces)
{
// Authenticate using created credentials
AuthenticationResult authenticationResult = null;
authenticationResult = await DoAuthentication("CustomerAppRegistration");
var tokenCredentials =
new TokenCredentials(authenticationResult.AccessToken, "Bearer");
using (var client = new PowerBIClient(
new Uri("https://api.powerbi.com/"), tokenCredentials))
{
try
{
foreach (var wksp in powerBIWorkspaces)
{
// Remove the user to the workspace.
await client.Groups.DeleteUserAsAdminAsync(new Guid(wksp.WorkspaceId), wksp.UserEmail);
}
}
catch (Exception ex)
{
var errorObject = new ResultObject();
errorObject.Success = false;
errorObject.ErrorMessage = ex.Message;
return errorObject;
}
}
var resultObject = new ResultObject();
resultObject.Success = true;
resultObject.ErrorMessage = "";
return resultObject;
}
private const string AuthorityFormat = "https://login.microsoftonline.com/{0}/v2.0";
private const string MSGraphScope = "https://analysis.windows.net/powerbi/api/.default";
private async Task<AuthenticationResult> DoAuthentication(string appRegistrationSection)
{
var config = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json").Build();
//var section = config.GetSection(nameof(AppRegistration));
var section = config.GetSection(appRegistrationSection);
var appRegistration = section.Get<AppRegistration>();
TenantID = appRegistration.TenantId;
ClientID = appRegistration.ClientId;
ClientSecret = appRegistration.ClientSecret;
IConfidentialClientApplication daemonClient;
daemonClient = ConfidentialClientApplicationBuilder.Create(ClientID)
.WithAuthority(string.Format(AuthorityFormat, TenantID))
.WithClientSecret(ClientSecret)
.Build();
AuthenticationResult authResult =
await daemonClient.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync();
return authResult;
}
I have an app that allows users to log in via facebook, once user enters their credentials - My api request saves the user onto the database and auto-generates a user token(This is unique to each user). In order to display user specific details once user logs in - the token needs to be referenced. I am trying to get this token to the PCL project but it returns null just for the token. When I tried passing another string like name, it passes the correct value. Any help will be much appreciated.Thanks
FacebookRender in droid:
public class FacebookRender : PageRenderer
{
public FacebookRender()
{
CustomerService customerService = new CustomerService();
String error;
var activity = this.Context as Activity;
var auth = new OAuth2Authenticator(
clientId: "",
scope: "",
authorizeUrl: new Uri("https://www.facebook.com/dialog/oauth/"),
redirectUrl: new Uri("https://www.facebook.com/connect/login_success.html")
);
auth.Completed += async (sender, eventArgs) =>
{
try
{
if (eventArgs.IsAuthenticated)
{
await AccountStore.Create().SaveAsync(eventArgs.Account, "FacebookProviderKey");
var accessToken = eventArgs.Account.Properties["access_token"].ToString();
var expiresIn = Convert.ToDouble(eventArgs.Account.Properties["expires_in"]);
var expiryDate = DateTime.Now + TimeSpan.FromSeconds(expiresIn);
var request = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me?fields=email,first_name,last_name,gender,picture"), null, eventArgs.Account);
var response = await request.GetResponseAsync();
var obj = JObject.Parse(response.GetResponseText());
var id = obj["id"].ToString().Replace("\"", "");
var name = obj["first_name"].ToString().Replace("\"", "");
var surname = obj["last_name"].ToString().Replace("\"", "");
var gender = obj["gender"].ToString().Replace("\"", "");
//var email = obj["email"].ToString().Replace("\"", "");
Customer.Customers cust = new Customer.Customers();
cust.Credentials = new Customer.Credentials();
cust.Name = name;
cust.Surname = surname;
cust.Email = "";
cust.MobilePhone = "";
cust.DOB = DateTime.Now;
cust.Number = "";
cust.City = "";
cust.Region = "";
cust.Country = "";
cust.DeviceToken = "sample";
cust.Credentials.SecretKey = "";
await customerService.AddCustomer(cust);
App.SaveToken(cust.Credentials.Token); - **//This is where I am passing the token**
App.NavigateToProfile(string.Format(name + surname));
}
else
{
App.NavigateToProfile("Invalid Login");
}
}
catch(Exception ex)
{
error = ex.Message;
}
};
activity.StartActivity(auth.GetUI(activity));
}
App.cs
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
public static void NavigateToProfile(string message)
{
App.Current.MainPage = (new Profile(message));
}
static string _Token;
public static string Token
{
get { return _Token; }
}
public static void SaveToken(string token)
{
_Token = token;
}
AboutPage.cs - I am passing the token in a label just to see if it's passing
public partial class About : ContentPage
{
private Label _lbltoken;
public About()
{
//InitializeComponent();
Appearing += (object s, EventArgs a) => {
_lbltoken.Text = App.Token;
};
string tk = App.Token;
_lbltoken = new Label()
{
FontSize = 20,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Text = tk,
};
var stack = new StackLayout
{
VerticalOptions = LayoutOptions.StartAndExpand,
Children = { _lbltoken },
};
Content = stack;
}
}
You can use the MessagingCenter.
Messages may be sent as a result like a button click, a system event or some other incident. Subscribers might be listening in order to change the appearance of the user interface, save data or trigger some other operation.
More Info
I don't really now if its good idea use static fields in App class. Xamarin access all fields with service locator, App.Current.[property] I will suggest you try to change these fields to public
string _Token;
public string Token
{
get { return _Token; }
}
public void SaveToken(string token)
{
_Token = token;
}
and use it with App.Current.SaveToken(token) or App.Current.Token
I am trying to test for failure conditions of my Account controller. When i run the test in debug mode, i am not seeing an expected result. I am expecting to return a failed identity result when reach the line of code to create a user async. however, in debug mode, it does not contain the error i provide it, and the success property is true. according to this site: https://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-rtm-140226/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/IdentityResult.cs?ImageName=Microsoft.AspNet.Identity.Core, the way i am going about this it "should" work.
what is the right way to setup this test so that when i hit UserManager.CreateAsync, it will return a Failed IdentityResult?
Test i am trying to run
[TestMethod]
public async Task AccountController_Post_register_valid_model_account_creation_fails_returns_exception_result()
{
// arrange
RegisterApiModel model = new RegisterApiModel
{
BusinessType = BusinessType.Architect,
City = "asdf",
CompanyName = "asdf",
Email = "asdf#asdf.com",
FirstName = "asdf",
JobTitle = "asdf",
LastName = "asdf",
OperatingDistance = 123,
Phone = "1231231234",
Password = "12345678",
PostalCode = "asdf",
PrimaryContactName = "asdf",
PrimaryContactPhone = "1231231234",
PrimaryContactTitle = "asdf",
StateId = 2
};
// create http request
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost.com/api/Account/Register");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "Companies" } });
// mock userstore
Mock<IUserStore<ApplicationUser>> userStore = new Mock<IUserStore<ApplicationUser>>();
userStore.Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>())).Returns(Task.FromResult(IdentityResult.Failed("Name " + model.Email + " already exists")));
var passwordManager = userStore.As<IUserPasswordStore<ApplicationUser>>();
ApplicationUserManager um = new ApplicationUserManager(userStore.Object);
um.PasswordValidator = pwValidator;
AccountController controller = new AccountController(um);
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
// act
var result = await controller.Register(model);
// assert
result.ShouldBeType(typeof(ExceptionResult));
}
web api method i am trying to test
public async Task<IHttpActionResult> Register([FromBody]RegisterApiModel model)
{
try
{
var company = new Company
{
Name = model.CompanyName,
CreateDate = DateTime.Now,
SubscriptionStatus = SubscriptionStatus.Free,
Address1 = model.Address1 ?? string.Empty,
Address2 = model.Address2 ?? string.Empty,
City = model.City,
StateId = model.StateId,
PostalCode = model.PostalCode,
BusinessType = model.BusinessType.Value,
OperatingDistance = model.OperatingDistance.Value,
Phone = PhoneNumber.ToStorage(model.Phone),
Fax = model.Fax == null ? string.Empty : PhoneNumber.ToStorage(model.Fax),
PrimaryContactName = model.PrimaryContactName,
PrimaryContactPhone = PhoneNumber.ToStorage(model.PrimaryContactPhone),
PrimaryContactTitle = model.PrimaryContactTitle
};
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName, Company = company, JobTitle = model.JobTitle };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// make user a company admin
user.Claims.Add(new Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim { ClaimValue = "Admin", ClaimType = "http://bidchuck.com/company/role", UserId = user.Id });
result = await UserManager.UpdateAsync(user);
if (result.Succeeded)
{
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Link("Default", new { controller = "Account", action = "ConfirmEmail", userId = user.Id, code = code });
await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking this link: link");
return Ok();
}
}
return BadRequest(result.Errors.First());
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
First of all, you are looking on source of Identity 2.2-alpha1 - it is not released yet. Better get decompiler (I use DotPeek from Jetbrains) and decompile assemblies you use in your project.
Then you are trying to test on too high level. Extract your method into class that is independent from your controllers:
UserService
{
public IdentityResult CreateUser(RegisterApiModel model, String urlCallback)
{
// don't forget to add generated code and userId as parameters into url
// do your user creation.
}
}
In your controller call this service:
public async Task<IHttpActionResult> Register([FromBody]RegisterApiModel model)
{
var urlCallbac = Url.Link("Default", new { controller = "Account", action = "ConfirmEmail" });
var result = await userService.CreateUserAsync(model, urlCallback);
if (result.Succeeded)
{
return Ok();
}
return BadRequest(result.Errors.First());
}
And test user Service separately from controllers. Your tests will become much more simple.
And at the moment it is very difficult to say why you are getting this result. Probably mocks are not completely set up to do what's needed to be done.
I am using JustMock and I am having difficulty having JustMock work with Entity Framework and mocking the database activity - specifically a fake insert
I want to do a fake insert and then return the fake inserted entity. I want to test that the password is hashed before insert (handled by the BLL).
UserBLL
public UserBLL(MyDatabaseEntities context)
: base(context)
{
}
public void Create(string email, string password)
{
//check and make sure that the email does not already exist
User user = GetUser(email);
if (user != null)
throw new Exception("Email account already exists");
string salt = GetSalt();
var hashedPassword = HashPassword(password, salt);
var newUser = new User()
{
EmailAddress = email,
Password = hashedPassword
};
context.Users.Add(newUser);
context.SaveChanges();
}
Here are my tests...
//This works
[Test]
[ExpectedException(ExpectedMessage = "Email account already exists")]
public void Create_NoDupeEmails()
{
List<User> fakeUsers = new List<User>();
fakeUsers.Add(new User { EmailAddress = "peter#email.com", FirstName = "Peter", LastName = "Griffin" });
fakeUsers.Add(new User { EmailAddress = "lois#email.com", FirstName = "Lois", LastName = "Griffin" });
//arrange
var context = Mock.Create<MyDatabaseEntities>();
Mock.Arrange(() => context.Users).ReturnsCollection(fakeUsers);
//act
var bll = new UserBLL(context);
bll.Create("peter#email.com", "password");
//Assert
//handled by the expected exception
}
Failing test
[Test]
public void Create_PasswordHashed()
{
var context = Mock.Create<MyDatabaseEntities>();
//arrange
Mock.Arrange(() => context.SaveChanges()).MustBeCalled();
var bll = new UserBLL(context);
bll.Create("test#email.com", "password");
User user = bll.GetUser("test#email.com");
//user is null?
Assert.IsTrue(user != null); //it fails here
Assert.IsTrue(user.Password != "password");
}
What am I doing wrong?
So I figured it out after looking at some samples across the web. The BLL remains the same, but here is the test
Once failing - now passing test
[Test]
public void Create_PasswordHashed()
{
//arrange
var context = Mock.Create<MyDatabaseEntities>();
var users = new List<User>();
Mock.Arrange(() => context.Users.Add(Arg.IsAny<User>()))
.DoInstead((User u) => users.Add(u)); //this will add the "user u" to the list of "users"
var bll = new UserBLL(context);
bll.Create("test#email.com", "password");
Assert.IsTrue(users.Count == 1);
Assert.IsTrue(!string.IsNullOrWhiteSpace(users[0].Password));
Assert.IsTrue(users[0].Password != "password");
}
I'm trying to get a unit test working for a service that is injecting items into the IHttpRequest.Items, using a request filter:
this.RequestFilters.Add((req, res, dto) =>
{
// simplified for readability...
var repo = container.Resolve<IClientRepository>();
var apiKey = req.Headers["ApiKey"];
// lookup account code from api key
var accountcode = repo.GetByApiKey(apiKey);
req.Items.Add("AccountCode", accountCode);
});
My service uses that dictionary item:
public class UserService : AppServiceBase
{
public IUserServiceGateway UserServiceGateway { get; set; }
public object Any(UserRequest request)
{
var accountCode = base.Request.Items["AccountCode"].ToString();
var user = UserServiceGateway.GetUserByUsername(request.Name);
return new UserResponse { User = user };
}
}
My test needs be somehow to mock the request, and insert that account code item:
[Test]
public void ValidUsernameReturnUser()
{
// arrange
var gateway = new Mock<IUserServiceGateway>();
gateway.Setup(s => s.GetUserByUsername(It.IsAny<string>()))
.Returns(new UserAccountDTO { Forename = "John", Surname = "Doe" });
var service = new UserService {
UserServiceGateway = gateway.Object,
RequestContext = new MockRequestContext(),
//Request = has no setter
};
// request is this case is null
base.Request.Items.Add("AccountCode", "DEF456");
// act
var response = (UserResponse)service.Any(new UserRequest { Name = "test" });
// assert
Assert.That(response.Result, Is.Not.Null);
}
The service itself accepts a mocked RequestContext, but not a Request. The test therefore fails. Is there a better way to do this?
I think this should do it.
[Test]
public void ValidUsernameReturnUser()
{
// arrange
var mockRequestContext = new MockRequestContext();
//add items to Request
mockRequestContext.Get<IHttpRequest>().Items.Add("AccountCode", "DEF456");
var gateway = new Mock<IUserServiceGateway>();
gateway.Setup(s => s.GetUserByUsername(It.IsAny<string>()))
.Returns(new UserAccountDTO { Forename = "John", Surname = "Doe" });
var service = new UserService {
UserServiceGateway = gateway.Object,
RequestContext = new MockRequestContext(),
};
// act
var response = (UserResponse)service.Any(new UserRequest { Name = "test" });
// assert
Assert.That(response.Result, Is.Not.Null);
}