Nunit Negative Tests Expected Exception - c#

I have a class that I check the index or the length of the string. And I want to write a Nunit Negative Test:
if the length of the string Out of Range the Nunit Test is true. Or the first index is digit "false" the Nunit Test is true.
What i try:
My CheckKeyClass:
public void SetKey(string keyToAnalyse)
{
Line = new string[keyToAnalyse.Length];
int nummeric;
bool num;
if (Line.Length == 0 || Line.Length < 24)
{
throw new Exception("Index Out of Range " + Line.Length);
}
// Ist Product a Character
num = int.TryParse(Line[0], out nummeric);
if (!num)
{
if (Line[0] == "K")
{
Product = 0;
}
}
else
{
throw new Exception("The Productnumber is not right: " + Line[0] ". \nPlease give a Character.");
}
}
My Nunit Test:
[Test]
public void NegativeTests()
{
keymanager.SetKey("KM6163-33583-01125-68785");
// Throws<ArgumentOutOfRangeException>(() => keymanager.Line[24]);
}
// ExpectedException Handling
public static void Throws<T>(Action func) where T : Exception
{
var exceptionThrown = false;
try
{
func.Invoke();
}
catch (T)
{
exceptionThrown = true;
}
if (!exceptionThrown)
{
throw new AssertFailedException(String.Format("An exception of type {0} was expected, but not thrown", typeof(T)));
}
}
So if the Line.length Out of Range the Test must be green also true.
How do I use that the Tests is true?
thx

Use Assert.Throws()
string keyHasLengthOf24 = "KM6163-33583-01125-68785";
var ex = Assert.Throws<Exception>(() => keymanager.SetKey(keyHasLengthOf24));
Assert.That(ex.Message, Is.EqualTo("Index Out of Range "));
See this SO answer for further detail.

Related

Testing null or empty string with this construction: s is null or ""

I am very perplexed by a piece of code I came across :
string s = "this is my dummy string, it could be anything";
if (s is null or "") { // ???
DoSomething();
}
I have never seen a string being tested in that manner.
I've always only seen this :
if(string.IsNullOrEmpty(s))
I've tried googling it but the keywords involved are too generic to find any relevant result.
So, what do you think? Is this a fancy new efficient way of testing strings, or is it just some quirky habit?
To have a good judgement about the performance.
I write these codes and run them separately.
The result is insightful
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Start");
string s = "this is my dummy string, it could be anything";
var d1 = DateTime.Now;
for(var i=0; i<1000000000;i++)
{
if(string.IsNullOrEmpty(s))
{
Console.WriteLine("Hello Again");
}
}
Console.WriteLine("Method1: " + (DateTime.Now - d1).TotalMilliseconds);
Console.WriteLine("End");
}
}
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Start");
string s = "this is my dummy string, it could be anything";
var d2 = DateTime.Now;
for(var i=0; i<1000000000;i++)
{
if (s is null or "")
{
Console.WriteLine("Hello Again");
}
}
Console.WriteLine("Method2: " + (DateTime.Now - d2).TotalMilliseconds);
Console.WriteLine("End");
}
}
Result (Duration in miliseconds):
Method1: 2959.476
Method2: 4676.6368
They're effectively the same:
https://learn.microsoft.com/en-us/dotnet/api/system.string.isnullorempty?view=net-5.0#remarks
Remarks
IsNullOrEmpty is a convenience method that enables you to
simultaneously test whether a String is null or its value is
String.Empty. It is equivalent to the following code:
bool TestForNullOrEmpty(string s)
{
bool result;
result = s == null || s == string.Empty;
return result;
}
string s1 = null;
string s2 = "";
Console.WriteLine(TestForNullOrEmpty(s1));
Console.WriteLine(TestForNullOrEmpty(s2));
// The example displays the following output:
// True
// True

C# How to convert time [ms, s, m, h, d] as string to seconds as int

I would like to know if there is a quick way (Maybe with TimeSpan) to convert a duration time formatted string like 17.24s or 20.79m or 1.3h to seconds format like 17 for 17.24s, 1260 for 20.79m and 4699 for 1.3h.
Thanks for suggestions.
Let's start from math:
20.79 minutes == 20.79 * 60 seconds == 1247 seconds
1.3 hours == 1.3 * 3600 seconds == 4680 seconds
I can't see 1260 or 4699, that's why I'll stick to simple math, I'll mark with \\TODO: the code which should be modified if you insist on different logic.
For different suffixes, let's extract model:
private static Dictionary<string, Func<double, TimeSpan>> s_Builders =
new Dictionary<string, Func<double, TimeSpan>>(StringComparer.OrdinalIgnoreCase) {
{ "", x => TimeSpan.FromSeconds(x)},
{ "s", x => TimeSpan.FromSeconds(x)},
//TODO: if you insist on 1260, put required logic here
{ "m", x => TimeSpan.FromMinutes(x)},
//TODO: if you insist on 4699, put required logic here
{ "h", x => TimeSpan.FromHours(x)},
{ "d", x => TimeSpan.FromDays(x)},
};
Time to implement TryMyParse method:
public static bool TryMyParse(string value, out TimeSpan result) {
result = default;
if (string.IsNullOrWhiteSpace(value))
return false;
string suffix = s_Builders
.Keys
.OrderByDescending(key => key.Length)
.FirstOrDefault(key => value.EndsWith(key, StringComparison.OrdinalIgnoreCase));
if (null == suffix)
return false;
else if (double.TryParse(value.Substring(0, value.Length - suffix.Length),
out double number)) {
try {
result = s_Builders[suffix](number);
return true;
}
catch (OverflowException) {
return false;
}
catch (ArgumentException) {
return false;
}
}
return false;
}
Finally, MyParse is very simple:
public static TimeSpan MyParse(string value) =>
TryMyParse(value, out var result)
? result
: throw new FormatException($"{value} is not a valid time");
Demo:
string[] tests = new string[] {
"17.24s",
"20.79m",
"1.3h",
};
string demo = string.Join(Environment.NewLine, tests
.Select(test => $"{test,6} :: {Math.Round(MyParse(test).TotalSeconds),4}"));
Console.Write(demo);
Outcome:
17.24s :: 17
20.79m :: 1247
1.3h :: 4680
Timespan does have a Parse method that accepts a string parameter but it uses a different input format than what you want. See more information here: https://learn.microsoft.com/en-us/dotnet/api/system.timespan.parse?view=netcore-3.1
You'll have to make your own parse method to convert a string input to a Timespan and use the TotalSeconds property to get the number of seconds.
If you go this route I would suggest starting by writing a unittest first containing all the inputs considered valid and their expected result. From there you can implement your parse method and verify it.
namespace StackOverflow
{
public class Tests
{
[Fact]
public void TestTimespanStringConversion()
{
// Test cases go here...
Assert.Equal(TimeSpan.FromHours(1.2), "1.20h".AsTimeSpan())
}
}
public static class StringExtensions
{
public static TimeSpan AsTimeSpan(this string input)
{
// Conversion logic goes here...
return new TimeSpan();
}
}
}
Thanks for the suggestions guys! I'm very appricate it. Here is my solution that workes for me:
private int timeParser(string pTime) {
int iResult = 0;
double dTime = 0.0;
NumberStyles style = NumberStyles.Number;
CultureInfo culture = CultureInfo.InvariantCulture;
if(pTime.Contains("ms")) {
if(Double.TryParse(pTime.Trim().Replace("ms", ""), style, culture, out dTime)) {
iResult = (int)Math.Round(TimeSpan.FromMilliseconds(dTime).TotalSeconds);
} else {
throw new FormatException("Unable to convert " + pTime);
}
} else if(pTime.Contains("s")) {
if(Double.TryParse(pTime.Trim().Replace("s", ""), style, culture, out dTime)) {
iResult = (int)Math.Round(TimeSpan.FromSeconds(dTime).TotalSeconds);
} else {
throw new FormatException("Unable to convert " + pTime);
}
} else if(pTime.Contains("m")) {
if(Double.TryParse(pTime.Trim().Replace("m", ""), style, culture, out dTime)) {
iResult = (int)Math.Round(TimeSpan.FromMinutes(dTime).TotalSeconds);
} else {
throw new FormatException("Unable to convert " + pTime);
}
} else if(pTime.Contains("h")) {
if(Double.TryParse(pTime.Trim().Replace("h", ""), style, culture, out dTime)) {
iResult = (int)Math.Round(TimeSpan.FromHours(dTime).TotalSeconds);
} else {
throw new FormatException("Unable to convert " + pTime);
}
} else if(pTime.Contains("d")) {
if(Double.TryParse(pTime.Trim().Replace("d", ""), style, culture, out dTime)) {
iResult = (int)Math.Round(TimeSpan.FromDays(dTime).TotalSeconds);
} else {
throw new FormatException("Unable to convert " + pTime);
}
} else {
throw new FormatException(pTime + " is not a valid timeformat");
}
return iResult;
}

Unit test case for If condition

I have a method IsValid:
public void IsValid(string applicationNumber)
{
if (!applicationNumber.Length.Equals(15))
{
throw new ApplicationException(string.Format("De lengte van de rubriek: aanvraagnummer [001.], met waarde {0}, is niet valide.", applicationNumber));
} else {
applicationNumberExpression = "qwerty123456789";
if (!applicationNumberExpression.IsMatch(applicationNumber))
{
throw new ApplicationException(string.Format("Het aanvraagnummer [001.] met waarde {0} is niet conform de voorgeschreven structuur.", applicationNumber));
}
}
}
I try to write a unit test case for isvalid, but i get some errors.
public void IsValidTestApplicationNumber()
{
var appno1 = new MFA.Convana.BusinessLayer.ObjectModel.ApplicationNumber();
MFA.Convana.BusinessLayer.ObjectModel.ApplicationNumber appno = new
MFA.Convana.BusinessLayer.ObjectStore.ApplicationNumber();
string applicationNumber = "qwerty123456789";
Assert.AreSame(appno.IsValid(applicationNumber);//error in thisline "no overload for method takes one argument"
}
Your primary mistake is attempting to assert on a function that returns void. This isn't possible.
You have two options:
a) Leave IsValid as returning void, and replace:
Assert.AreSame(appno.IsValid(applicationNumber);
with:
appno.IsValid(applicationNumber);
OR
b) Change IsValid to return a bool. To do this, change:
Assert.AreSame(appno.IsValid(applicationNumber);
to either:
Assert.IsTrue(appno.IsValid(applicationNumber));
(depending on what you want the test to do)
Also, change:
public void IsValid(string applicationNumber)
{
if (!applicationNumber.Length.Equals(15))
{
throw new ApplicationException(string.Format("De lengte van de rubriek: aanvraagnummer [001.], met waarde {0}, is niet valide.", applicationNumber));
} else {
applicationNumberExpression = "qwerty123456789";
if (!applicationNumberExpression.IsMatch(applicationNumber))
{
throw new ApplicationException(string.Format("Het aanvraagnummer [001.] met waarde {0} is niet conform de voorgeschreven structuur.", applicationNumber));
}
}
}
to:
public bool IsValid(string applicationNumber)
{
if (!applicationNumber.Length.Equals(15))
{
throw new ApplicationException(string.Format("De lengte van de rubriek: aanvraagnummer [001.], met waarde {0}, is niet valide.", applicationNumber));
} else {
applicationNumberExpression = "qwerty123456789";
if (!applicationNumberExpression.IsMatch(applicationNumber))
{
throw new ApplicationException(string.Format("Het aanvraagnummer [001.] met waarde {0} is niet conform de voorgeschreven structuur.", applicationNumber));
}
}
return true;
}
no overload for method takes one argument
Clearly error says everything ...
Assert.AreSame(appno.IsValid(applicationNumber), /*TO WHAT SHOULD IT BE COMPARED*/);
Assert.AreSame(appno.IsValid(applicationNumber), false);
EDIT :
Extending this answer to be more accurate and on topic.
You're trying to compare two values but using reference comparer ( Assert.AreSame ).
This can fail because the 2 references are not the same ( read about references here ).
What you should do is to compare the two values. To do this you have to use different assertion methods.
For your example you can use Assert.IsTrue if you expect your method to return true or Assert.IsFalse if your expected value is false:
bool result = meObj.IsValid("someString");
// let's say you're expecting false
Assert.IsFalse(result);
// but if you're expecting true then change IsFalse to IsTrue
Or you can use AreEqual which in your case will be Assert.AreEqual<bool> :
bool result = meObj.IsValid("someString");
bool expected = false;
Assert.AreEqual(expected, result);
[TestClass]
public class UnitTest_MailClient
{
[TestMethod]
public void IsValid_TestApplicationNumber()
{
MFA.Convana.BusinessLayer.ObjectModel.ApplicationNumber appno = new MFA.Convana.BusinessLayer.ObjectStore.ApplicationNumber();
String Input;
String exception;
String TestCase;
XMLHelper TestData = new XMLHelper();
TestCase = TestData.GetTestDataString("IsValid");
String[] arr = TestCase.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
if (arr.Length > 0 && TestCase.Length > 0)
{
for (int i = 0; i < arr.Length; i++)
{
Input = arr[i].Substring(0, arr[i].IndexOf(TestData.FieldDelimiter));
exception = arr[i].Substring(arr[i].IndexOf(TestData.FieldDelimiter) + 3);
try
{
appno.IsValid(Input);
Assert.IsTrue(true);
}
catch (Exception ex)
{
Assert.AreEqual(exception, ex.Message);
}
}
}
else
{
Assert.Inconclusive("Test Skipped as no test data");
}
}

C# ValidationResult value equality assertion fails with Assert.AreEqual

I have a simple ValidationRule implementation :
public class IntegerRangeRule : ValidationRule
{
public Int32 Max { get; set; }
public Int32 Min { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
int integer;
if(string.IsNullOrEmpty((string)value))
{
return new ValidationResult(false, "Field cannot be empty");
}
if (Int32.TryParse((string) value, out integer))
{
if((integer < Min) || (integer > Max))
return new ValidationResult(false, string.Format("Enter a value between {0} and {1}.", Min, Max));
}
else
{
return new ValidationResult(false, "Illegal characters: " + (string)value);
}
return ValidationResult.ValidResult;
}
}
I wrote the following unit test for the Validation method:
[TestMethod()]
public void ValidateTest_InputTooSmall()
{
//Setup
var target = new IntegerRangeRule()
{
Max = 100,
Min = 1
};
var expected = new ValidationResult(false, "Enter a value between 1 and 100.");
//Exercise
var actual = target.Validate("0", null);
//Verify
Assert.AreEqual(expected.ErrorContent, actual.ErrorContent);
Assert.AreEqual(expected.IsValid, actual.IsValid);
Assert.AreEqual(expected.GetHashCode(), actual.GetHashCode());
Assert.AreEqual(expected, actual);
}
Here is the catch.
The first three assertions all pass. But the last one fails.
If I replace the
...
if (Int32.TryParse((string) value, out integer))
{
if((integer < Min) || (integer > Max))
return new ValidationResult(false, string.Format("Enter a value between {0} and {1}.", Min, Max));
}
...
with a constant string.
...
if (Int32.TryParse((string) value, out integer))
{
if((integer < Min) || (integer > Max))
return new ValidationResult(false, "Enter a value between 1 and 100.");
}
...
The last assertion will pass.
I dont understand what is causing the problem here. Thank you.
ValidationResult contains this in Equals:
return (IsValid == vr.IsValid) && (ErrorContent == vr.ErrorContent);
Since ErrorContent is an object, this is a reference comparison, not a value comparison. In your case you are generating a new string using String.Format and comparing that reference to a literal, so the result is always false. When you change it to the same literal string it passes because literals are interned by the CLR.
Meanwhile your testing framework is probably calling Object.Equals(object, object) inside its Assert.AreEqual method, which invokes String's overridden Equals method. This does a value comparison.

Find exception hiding/swallowing in C# code in VS2013

is there some way built in function/extension/tool to find all exception hidings/exception swallowing in C# solution(ASP.NET WebForms)n in VS2013.
Thanks
EDIT:
I have existing solution in which some programmers use hide/swallow exceptions(empty catch, catch only with some useless code). And I am looking for some way to find all these places in code, analyze them, and then fix them.
You can write some code using Roslyn to handle this pretty easily.
I actually wrote some code to do exactly that for a friend. It was my first attempt at using the Roslyn SDK, so my code is probably a terrible mess, but it was definitely functional.
static void Main(string[] args)
{
var result = Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseFile(#"..\..\Test.cs");
var root = result.GetRoot();
var exceptionNodes = FindCatchNodes(root);
foreach (var node in exceptionNodes)
{
var line = node.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
if (IsTotallyEmptyCatch(node))
{
Console.WriteLine("Totally empty catch: line {0}", line);
}
if (JustRethrows(node))
{
Console.WriteLine("Pointless rethrow: line {0}", line);
}
}
}
static List<SyntaxNodeOrToken> FindCatchNodes(SyntaxNodeOrToken node)
{
var exceptions = new List<SyntaxNodeOrToken>();
var isCatchBlock = node.IsKind(SyntaxKind.CatchClause);
if (isCatchBlock)
{
exceptions.Add(node);
}
foreach (var result in node.ChildNodesAndTokens().Select(FindCatchNodes).Where(result => result != null))
{
exceptions.AddRange(result);
}
return exceptions;
}
static bool IsTotallyEmptyCatch(SyntaxNodeOrToken catchBlock)
{
var block = catchBlock.ChildNodesAndTokens().First(t => t.CSharpKind() == SyntaxKind.Block);
var children = block.ChildNodesAndTokens();
return (children.Count == 2 && children.Any(c => c.CSharpKind() == SyntaxKind.OpenBraceToken) &&
children.Any(c => c.CSharpKind() == SyntaxKind.CloseBraceToken));
}
static bool JustRethrows(SyntaxNodeOrToken catchBlock)
{
var block = catchBlock.ChildNodesAndTokens().First(t => t.CSharpKind() == SyntaxKind.Block);
var children = block.ChildNodesAndTokens();
return (children.Count == 3 && children.Any(c => c.CSharpKind() == SyntaxKind.OpenBraceToken) &&
children.Any(c => c.CSharpKind() == SyntaxKind.CloseBraceToken) && children.Any(c=>c.CSharpKind() == SyntaxKind.ThrowStatement));
}
Given this test file:
using System;
namespace RoslynTest
{
public class Test
{
public void Foo()
{
try
{
var x = 0;
}
catch
{
}
}
public void Bar()
{
try
{
var x = 0;
}
catch (Exception ex)
{
throw;
}
}
public void Baz()
{
try
{
var x = 0;
}
catch (Exception ex)
{
throw ex;
}
}
}
}
The output is:
Totally empty catch: ....\Test.cs: line 12
Pointless rethrow: ....\Test.cs: line 24
Pointless rethrow: ....\Test.cs: line 37
I don't know about the built-in methods. But you can write your own tool to find such places. Just regex all your files in solution and count catch and throw. There should be the same amount for each file :)

Categories