How can I refactor my C# code to be easily testable? - c#

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?

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
}

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.

Related

Generic TaskExecution Method with Variable Parameter functions & return types

I am trying to refactor Parallel Task Executor on 2 methods which executes on Variable Parameters & return types. can assume that both methods have same input parameters and return types but they can change.
For Eg: MyObject Task1(string,string,int), MyObject Task2(string,string,int) is valid
MyObject3 Task1(string,string,int, double), MyObject3 Task2(string,string,int,double) is valid too
currently, I am implementing it as follows. This works for one or two methods. But now I hit a case I need to do this for around 15 methods. How do I go about refactoring into its own methods and just calling it as RunTaskInParallel<ReturnTypeOfBothMethods>(Task1(params...), Task2(params...))
we can assume both methods (Task1, Task2) have same exact parameters & return types.
public async Task<UserPolicies> GetUserPoliciesAsync(string userId, string tenantId, IList<UserAssignedSettingType> userPoliciesToFetch)
{
if (FlagManager.GetSettingValue<bool>(FlagConstants.EnableMyFeature, tenantId, logSettingValue: false)
&& userPoliciesToFetch.Contains(UserAssignedSettingType.UserPermission))
{
//Read tenant Status from cache or Backend.
var tenantStatus = await GetTenantStatusFromCacheOrBackendAsync(tenantId);
if (tenantStatus.IsFlighted && tenantStatus.IsMigrated.Equals(AAEConstants.TenantMigrationCompleted, StringComparison.OrdinalIgnoreCase)) //Tenant signedup & Migrated completely to AAE Flow
{
//This is the Part that needs to be repeated for different method types.
//Here its calling OtherService Method (GetUserPoliciesAsync) and custom Logic for Man in Middle method (GetAppDocFromCacheOrBackendAsync)
var taskList = new List<Task>()
{
_policyBackendProxy.GetUserPoliciesAsync(userId, tenantId, userPoliciesToFetch)
};
//Preparing Default User teamsAppPerm document in case of BE request fail or tenantStatus.ResolutionModel == default.
TeamsAppPermissionPolicy teamsAppPermissionDocFromAAE = new TeamsAppPermissionPolicy();
if (tenantStatus.ResolutionModel.Equals(AAEConstants.TenantResolutionModelCustom, StringComparison.OrdinalIgnoreCase))
{
//only call AAE Cache or Backend for tenantStatus.ResolutionModel == Custom
taskList.Add(GetAppDocFromCacheOrBackendAsync(userId, tenantId));
}
var allTasks = Task.WhenAll(taskList);
await allTasks;
var otherPolicies = taskList[0].IsFaulted ?
new UserPolicies() : ((Task<UserPolicies>)taskList[0]).Result;
if (taskList.Count > 1) //Making sure we only read response if we hit AAE Backend. In which case tasklist length is 2.
{
teamsAppPermissionDocFromAAE = taskList[1].IsFaulted ?
new TeamsAppPermissionPolicy() : ((Task<TeamsAppPermissionPolicy>)taskList[1]).Result;
}
otherPolicies.AppsPermissionsPolicy = teamsAppPermissionDocFromAAE.ToMTTeamsAppPermissionPolicy().ToMTPolicyDocument();
//Wrap AAE TeamsApp doc response into other Policies
return otherPolicies;
}
else
{
//Tenant has not signed up or not migrated. Use existing PolicyService flow
return await _policyBackendProxy.GetUserPoliciesAsync(userId, tenantId, userPoliciesToFetch);
}
}
else
{
//AAE feature is not enabled in ECS aka Kill Switch
return await _policyBackendProxy.GetUserPoliciesAsync(userId, tenantId, userPoliciesToFetch);
}
}
Here _policyBackendProxy is the original flow and I only interject its flow if certain conditions are met which are my if Statements. else go by previous flow.
This method returns UserPolicies object but there are few other methods I need to parallel execute old method or my corresponding new method but do not want to repeat the same logic for diff methods and return types.
Another similar method but returning diff type is below.
public async Task<AcmsSettings> GetTenantDefaultPoliciesAsync(string tenantId, IList<UserAssignedSettingType> userPoliciesToFetch)
{
if (_flagManager.GetSettingValue<bool>(EcsSettingsConstants.EnableAppCatalogService, tenantId, logSettingValue: false)
&& userPoliciesToFetch.Contains(UserAssignedSettingType.AppsPermission))
{
var tenantStatus = await GetTenantStatusFromCacheOrBackendAsync(tenantId);
if (tenantStatus.IsFlighted
&& tenantStatus.IsMigrated.Equals(AAEConstants.TenantMigrationCompleted, StringComparison.OrdinalIgnoreCase)) //Tenant signedup & Migrated completely to AdminAppCatalog Flow
{
var taskList = new List<Task>();
if (tenantStatus.ResolutionModel.Equals(AAEConstants.TenantResolutionModelCustom, StringComparison.OrdinalIgnoreCase))
{
taskList.Add(GetTeamsAppDocFromCacheOrBackendAsync(null, tenantId));
}
//Preparing Default User teamsAppPerm document in case of BE request fail or tenantStatus.ResolutionModel == default.
TeamsAppPermissionPolicy teamsAppPermissionDocFromAAE = new TeamsAppPermissionPolicy();
var allTasks = Task.WhenAll(taskList);
await allTasks;
var otherPolicies = taskList[0].IsFaulted ?
new AcmsSettings() : ((Task<AcmsSettings>)taskList[0]).Result;
if (taskList.Count > 1) //Making sure we only read response if we hit AAE Backend. In which case tasklist length is 2.
{
teamsAppPermissionDocFromAAE = taskList[1].IsFaulted ?
new TeamsAppPermissionPolicy() : ((Task<TeamsAppPermissionPolicy>)taskList[1]).Result;
}
otherPolicies.AppsPermissionPolicy = teamsAppPermissionDocFromAAE.ToMTTeamsAppPermissionPolicy().ToMTPolicyDocument();
return otherPolicies;
}
else
{
return await _policyBackendProxy.GetTenantDefaultPoliciesAsync(tenantId, userPoliciesToFetch);
}
}
else
{
return await _policyBackendProxy.GetTenantDefaultPoliciesAsync(tenantId, userPoliciesToFetch);
}
}
The above method is similar to the first one except its taking diff parameters (string tenantId, IList<UserAssignedSettingType> userPoliciesToFetch) and returning diff type AcmsSettings.
I have to repeat this logic for every method in interface. I am certain there is a better and efficient way but not sure what is it.

Exception message in xunit includes parameter so my test fails

I am trying to check that an exception that I throw gives the correct error message.
I have a method in a class that withdraws (substracts) from a value. If the value is less than 0, I throw an exception.
if (amount < 0)
{
throw new System.ArgumentOutOfRangeException("amount", AmountLessThanZeroMessage);
}
This is my error message:
public const string AmountLessThanZeroMessage = "Amount is less than zero";
However, when I try to write a unit test to see if I get the correct message, it fails because of the parameter. This is the test:
[Fact]
public void CannotWithdrawLessThanZero()
{
// Arrange
var account = new Account("User", 23);
// Act
account.DepositCash(100);
var thrownException = Assert.Throws<ArgumentOutOfRangeException>(() => account.WithdrawCash(-10));
// Assert
Assert.Equal(Account.AmountLessThanZeroMessage, thrownException.Message);
}
The result includes the parameter in the end, resulting in a failed test:
It seems the actual message includes which parameter it refers to. How do I correct this message? Should I just add the line (Parameter 'amount') to the expected string, or are there any better options?
You can create the exact same exception and use its message property. Like the code below
[Fact]
public void CannotWithdrawLessThanZero()
{
// Arrange
var account = new Account("User", 23);
var expectedException = new System.ArgumentOutOfRangeException("amount", AmountLessThanZeroMessage);
// Act
account.DepositCash(100);
var thrownException = Assert.Throws<ArgumentOutOfRangeException>(() => account.WithdrawCash(-10));
// Assert
Assert.Equal(expectedException.Message, thrownException.Message);
}
Messages changes often, message can be localized, so testing message equality will make tests more fragile.
Instead you can test that correct exception type was thrown, or even better and create domain specific exception to be sure that exception have been thrown for correct reasons.
public class WithdrawOfNegativeAmountNotAllowedException : Exception
{
public WithdrawOfNegativeAmountNotAllowedException(int amount)
: base($"Amount is less than zero ({amount})")
{
}
}
[Fact]
public void Cannot_withdraw_less_than_zero()
{
var account = new Account("User", 23);
account.DepositCash(100);
Action withdraw = () => account.WithdrawCash(-10);
withdraw.Should().Throw<WithdrawOfNegativeAmountNotAllowedException>();
}
In case you still want to test for correct message. Assertions of strings can be done in multiple ways, I would suggest to make assertion as loose as possible.
Test for part of the message which unlikely will change. For example test that message starts with some text or contain critical value.
[Fact]
public void Cannot_withdraw_less_than_zero()
{
var account = new Account("User", 23);
account.DepositCash(100);
Action withdraw = () => account.WithdrawCash(-10);
withdraw.Should()
.Throw<WithdrawOfNegativeAmountNotAllowedException>()
.And
.Message.Should().ContainEquivalentOf("Amount is less than zero");
}

Standarised Validation and Error checking C#

I am currently developing a project which handles a lot of types of inputs which have constraints like a double being between 0 and 1. I am thinking of validating this input in a standardised way by using a class which I would call to validate the input and if the input was wrong would throw out a result to a log file or throw an exception.
tl;dr : Is there a good method to standardise validation across a program which uses a log file.
Example:
public void AddTimes(double time1, double time2)
{
if (time1 < 0)
{
ErrorManager.Validation_MustBeNonNegative(ErrorManager.GetName(new { time1 }), time1.ToString());
}
else if (time2 <= 0)
{
ErrorManager.Validation_MustBeNonNegativeNonZero(ErrorManager.GetName(new { time1 }), time2.ToString());
}
}
You can use log4net to do the logging from your custom method. Configuring and logging to log4net is a well documented topic on SO and so you should refine your question and search again.
To validate your value, you need to have a way of expressing the valid range. Microsoft includes some range checking features in it's frameworks, but you can easily roll your own:
public static void AssertTimesAreValid(double time1, double time2)
{
if (time1 < 0)
{
ErrorManager.Validation_MustBeNonNegative(ErrorManager.GetName(new { time1 }), time1.ToString());
throw new ArgumentException("Times are not far enough apart.");
}
else if (time2 <= 0)
{
ErrorManager.Validation_MustBeNonNegativeNonZero(ErrorManager.GetName(new { time1 }), time2.ToString());
throw new ArgumentException("Times are not far enough apart.");
}
}
public void AddTimes(double time1, double time2)
{
AssertTimesAreValid(time1, time2)
// Do your work now
}
Your ErrorManager will need to do the logging.

Checking if HttpStatusCode represents success or failure

Let's suppose I have the following variable:
System.Net.HttpStatusCode status = System.Net.HttpStatusCode.OK;
How can I check if this is a success status code or a failure one?
For instance, I can do the following:
int code = (int)status;
if(code >= 200 && code < 300) {
//Success
}
I can also have some kind of white list:
HttpStatusCode[] successStatus = new HttpStatusCode[] {
HttpStatusCode.OK,
HttpStatusCode.Created,
HttpStatusCode.Accepted,
HttpStatusCode.NonAuthoritativeInformation,
HttpStatusCode.NoContent,
HttpStatusCode.ResetContent,
HttpStatusCode.PartialContent
};
if(successStatus.Contains(status)) //LINQ
{
//Success
}
None of these alternatives convinces me, and I was hoping for a .NET class or method that can do this work for me, such as:
bool isSuccess = HttpUtilities.IsSuccess(status);
If you're using the HttpClient class, then you'll get a HttpResponseMessage back.
This class has a useful property called IsSuccessStatusCode that will do the check for you.
using (var client = new HttpClient())
{
var response = await client.PostAsync(uri, content);
if (response.IsSuccessStatusCode)
{
//...
}
}
In case you're curious, this property is implemented as:
public bool IsSuccessStatusCode
{
get { return ((int)statusCode >= 200) && ((int)statusCode <= 299); }
}
So you can just reuse this algorithm if you're not using HttpClient directly.
You can also use EnsureSuccessStatusCode to throw an exception in case the response was not successful.
The accepted answer bothers me a bit as it contains magic numbers, (although they are in standard) in its second part. And first part is not generic to plain integer status codes, although it is close to my answer.
You could achieve exactly the same result by instantiating HttpResponseMessage with your status code and checking for success. It does throw an argument exception if the value is smaller than zero or greater than 999.
if (new HttpResponseMessage((HttpStatusCode)statusCode).IsSuccessStatusCode)
{
// ...
}
This is not exactly concise, but you could make it an extension.
I am partial to the discoverability of extension methods.
public static class HttpStatusCodeExtensions
{
public static bool IsSuccessStatusCode(this HttpStatusCode statusCode)
{
var asInt = (int)statusCode;
return asInt >= 200 && asInt <= 299;
}
}
As long as your namespace is in scope, usage would be statusCode.IsSuccessStatusCode().
The HttpResponseMessage class has a IsSuccessStatusCode property, looking at the source code it is like this so as usr has already suggested 200-299 is probably the best you can do.
public bool IsSuccessStatusCode
{
get { return ((int)statusCode >= 200) && ((int)statusCode <= 299); }
}
Adding to #TomDoesCode answer If you are using HttpWebResponse
you can add this extension method:
public static bool IsSuccessStatusCode(this HttpWebResponse httpWebResponse)
{
return ((int)httpWebResponse.StatusCode >= 200) && ((int)httpWebResponse.StatusCode <= 299);
}
It depends on what HTTP resource you are calling. Usually, the 2xx range is defined as the range of success status codes. That's clearly a convention that not every HTTP server will adhere to.
For example, submitting a form on a website will often return a 302 redirect.
If you want to devise a general method then the code >= 200 && code < 300 idea is probably your best shot.
If you are calling your own server then you probably should make sure that you standardize on 200.
This is an extension of the previous answer, that avoids the creation and subsequent garbage collection of a new object for each invocation.
public static class StatusCodeExtensions
{
private static readonly ConcurrentDictionary<HttpStatusCode, bool> IsSuccessStatusCode = new ConcurrentDictionary<HttpStatusCode, bool>();
public static bool IsSuccess(this HttpStatusCode statusCode) => IsSuccessStatusCode.GetOrAdd(statusCode, c => new HttpResponseMessage(c).IsSuccessStatusCode);
}

How do I verify whether one of two methods has been called?

I am using Moq to verify if a method is being called in my unittest. In this specific case I want to test if the method under test logs an Error through log4net. The problem is, this can be done by either calling log.Error or log.ErrorFormat. Either is fine.
How can I verify this though? I only know how to verify that they have both been called.
var logMock = new Mock<ILog>();
var myClass = new MyClass(logMock.Object);
myClass.MyMethod();
logMock.Verify(log => log.Error(It.IsAny<object>()));
logMock.Verify(log => log.ErrorFormat(It.IsAny<string>(), It.IsAny<object>()));
Now that I think of it, they both have a bunch of overloads, I don't mind if any of the overloads are called either (I'm starting to doubt this is a good test).
Thanks in advance.
EDIT: I just thought of something nasty:
try
{
logMock.Verify(log => log.Error(It.IsAny<object>()));
}
catch (Moq.MockException ex)
{
logMock.Verify(log => log.ErrorFormat(It.IsAny<string>(), It.IsAny<object>()));
}
Maybe I can wrap this in some kind of extension method... e.g. VerifyAny.
You could register a callback for each valid error method that sets a flag:
// Arrange
bool errorFlag = false;
logMock
.Setup(l => l.Error(It.IsAny<object>()))
.Callback((object o) => errorFlag = true);
/* repeat setup for each logMock method */
// Act
myClass.MyMethod();
// Assert
Assert.IsTrue(errorFlag);
Of course, this will still be tedious if you have many overloads to cover.
EDIT: And for fun, here's an extension method for Mock<T>.VerifyAny:
public static class MockExtensions
{
public static void VerifyAny<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions)
where T: class
{
List<MockException> exceptions = new List<MockException>();
bool success = false;
foreach (var expression in expressions)
{
try
{
mock.Verify(expression);
success = true;
break;
}
catch (MockException ex)
{
exceptions.Add(ex);
}
}
if (!success)
{
throw new AggregateException("None of the specified methods were invoked.", exceptions);
}
}
}
Usage:
[TestMethod]
public void FooTest()
{
Mock<IFoo> fooMock = new Mock<IFoo>();
fooMock.Object.Bar1();
fooMock.VerifyAny(
f => f.Bar1(),
f => f.Bar2());
}
if you are specifically testing that a specific error was logged, why not have 2 tests, one that ensure that log.Error is called and one that ensure that log.ErrorFormat is called, I am assuming that you can control which one is called based on the input.
if you still wanna verify one or the other, you can just use this approach, it does exactly what you need:
Verify that either one method or the other was invoked in a unit test

Categories