I have written below code:
public RequestValidationResultBase Validate(ParRequest parRequest, Client client)
{
if (client is null) throw new ArgumentNullException(nameof(client));
// validate whether client is allowed to use PAR
if (!IsStringContainedInArray(GrantTypes.AuthorizationCode, client.GrantTypes.Select(p => p.GrantType).ToArray()))
return new(new InvalidGrantType());
if (parRequest is null || HasAnyNullProperty(parRequest, nameof(ParRequest.Prompt), nameof(ParRequest.ApiScope)))
return new(new InvalidRequest());
// validate whether requested callback uri is valid for given client
if (!IsStringContainedInArray(parRequest.CallbackUri, client.Callbacks.Select(p => p.Uri).ToArray()))
return new(new InvalidCallbackUri());
// validate whether requested api scopes are valid for given client
// NOTE that we want to allow requests without api scopes (some clients may need user identity only)
if (!string.IsNullOrEmpty(parRequest.ApiScope)
&& !IsFirstArrayContainedInSecondArray(parRequest.ApiScope.Split(' '), client.AllowedApiScopes.Select(p => p.Name).ToArray()))
return new(new InvalidApiScope());
// we want to ignore refresh_token api scope when prompt "consent" is not present
if (!string.IsNullOrEmpty(parRequest.ApiScope) && parRequest.ApiScope.Split(' ').Contains("offline_access")
&& (string.IsNullOrEmpty(parRequest.Prompt) || !parRequest.Prompt.Split(' ').Contains(PromptTypes.Consent)))
parRequest.ApiScope = parRequest.ApiScope.Replace("offline_access", string.Empty).Trim();
// validate whether requested identity resources are valid for given client
if (!IsFirstArrayContainedInSecondArray(parRequest.IdentityResource.Split(' '),
client.AllowedIdentityResources.Select(p => p.Name).ToArray()))
return new(new InvalidIdentityResource());
// validate whether requested prompt is valid
if (!PromptTypes.ValidatePrompt(parRequest.Prompt)) return new(new InvalidPrompt());
if (!IsStringBase64Url(parRequest.State, 32, 160)) return new(new InvalidState());
if (!IsStringBase64Url(parRequest.Nonce, 32)) return new(new InvalidState());
if (!IsStringBase64Url(parRequest.CodeChallenge, 32)) return new(new InvalidCodeChallenge());
if (!IsStringBase64Url(parRequest.AuthChallenge, 32)) return new(new InvalidAuthChallenge());
return new() { Valid = true };
}
And I don't have any idea how can I write unit tests for this.
If I want to test e.g. result when prompt is invalid, then all steps above prompt validation must be valid.
I can't mock specific if
conditions and even if I would pack all these if
condition into small private methods then I still can't mock private methods. So how can I write unit tests for this code or how should I refactor this code to be easily testable?
Or maybe the only solution is to create a ParRequest
object that gets to a certain point in the validation?
CodePudding user response:
I can't mock specific if conditions
You don't need to mock the ifs, each test case can pass a progressively more valid input with the same setup for all the test cases.
CodePudding user response:
Each cluster of if statements used for validation could be moved into public methods inside a validation class.
You can then test each validation method rigorously with various test cases and then test the original method can just have one or two general test cases.
e.g. something like this:
public void LongMethodWithLotsOfValidation()
{
if (_age < 10)
//do something
if (_height > 6)
//do something
if (_experience > 10)
//do something
if (_money > 100)
//do something
}
Can be refactored to something like this:
public static class Validation
{
private const AgeLimit = 10;
private const MinHeight = 6;
private const MinYearsExperience = 10;
private const MinMoney = 100;
public static bool IsAgeLowEnough(int age)
{
return age < AgeLimit;
}
public static bool IsHeightGreatEnough(int age)
{
return height > MinHeight;
}
public static bool IsExperienceEnough(int age)
{
return experience > MinYearsExperience;
}
public static bool IsMoneyEnough(int age)
{
return money > MinMoney;
}
}
And the validation class can be used like so:
public void ImprovedMethod()
{
if (Validation.IsAgeLowEnough(_age))
//do something
if (Validation.IsHeightGreatEnough(_height))
//do something
if (Validation.IsExperienceEnough(_experience))
//do something
if (Validation.IsMoneyEnough(_money))
//do something
}