I have a method. I want to test the method with a unit test. What's a good way to test that method?
public class ChoiceOption
{
public static int Choice(string message, string errorMessage)
{
while (true)
{
try
{
Console.Write(message);
string userInput = Console.ReadLine();
if (userInput == string.Empty)
{
userInput = "0";
}
return int.Parse(userInput);
}
catch (Exception)
{
Console.WriteLine(errorMessage);
}
}
}
}
First of all, rewrite your method so it can be unit tested and doesn't rely on Console input and output, e.g.
public class ChoiceOption
{
private readonly Func<string> readLine;
private readonly Action<string> writeLine;
public ChoiceOption(Func<string> readLine, Action<string> writeLine)
{
this.readLine = readLine;
this.writeLine = writeLine;
}
public int Choice(string message, string errorMessage)
{
while (true)
{
writeLine(message);
string userInput = readLine();
if (string.IsNullOrEmpty(userInput)) return 0;
if (int.TryParse(userInput, out int result))
{
return result;
}
writeLine(errorMessage);
}
}
}
Now you can inject methods for read and writeline that inject test data and collect the output to check it. Take a look at the nuget package Moq also as way to create those mock objects for testing.
[TestFixture]
public class TestMethod
{
[TestCase("", ExpectedResult = "Message\n0")]
[TestCase("123", ExpectedResult = "Message\n123")]
[TestCase("A\n1234", ExpectedResult = "Message\nError\nMessage\n1234")]
public string MethodReturnsExpected(string input)
{
// Create mock objects for testing that simulate user input
int readPos = 0;
string output = "";
Func<string> readline = () => input.Split('\n')[readPos++];
Action<string> writeline = (line) => output = output + line + "\n";
// Create the 'unit under test'
var uut = new ChoiceOption(readline, writeline);
int result = uut.Choice("Message", "Error");
// Return something that can be verified
return output + result;
}
}
First, you should change the method to a pure function to make testing easier (e.g. no mocking, etc.), so you need to extract the user input outside the method and ideally also printing to the console. Now, you can emulate the user input from your test and also assert the exception:
public class ChoiceOption
{
public static int Choice(string userInput, string message, string errorMessage)
{
if (userInput == string.Empty)
{
userInput = "0";
}
return int.Parse(userInput);
}
}
Now, it's easily testable. The loop, the user input and catching of the exception(s) will be in the caller of this method. Which is your production code and also your unit test(s).
The first things you should cover are the expected, "correct" cases. In this case, that would be a few numbers (probably 0, a positive number, and a negative number).
At that point, you then try edge cases:
"hello"
'c'
2.5
2^63
true
" "
Those are immediate examples I can think of, but ultimately your unit test is as strong as you are creative
Related
Write each sentence of the text in ascending order of the number of vowels in a word.error: none of the overloads of the "Letters" method accept arguments "1".
public class Vowels
{
public string Letters()
{
string[] a = { "aeiouy" };
Array.Sort(a);
}
}
Unit-tests:
[TestClass]
public class VowelsTests
{
[TestMethod]
public void MethodSort_vowels_in_ascending_order()
{
string a = "Teest! Test!";
string expected = "Test! Teest";
Vowels t = new Vowels();
int actual = t.Letters(a);
Assert.AreEqual(expected, actual);
}
}
You are calling the method Letters() with a parameter (t.Letters(a)), but it is defined without any parameters (public string Letters() {}).
I have a very basic method that divides two double values.
For unit testing I want to include a invalid input (string) to throw error message or exception. What is most simple way to parse the value or fail the test (expected)?
CalculatorClass.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Calculator
{
public class CalculatorClass
{
//METHODS
public double Divide(double num1, double num2)
{
double result = num1 / num2;
return result;
}
}
}
UnitTest1.cs
using System;
using Calculator; //ADD REFERENCE
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CalcMethodTest
{
//AreEqual
//AreNotEqual
//AreNotSame
//AreSame
//Equals
//Fail
//Inconclusive
//IsFalse
//IsInstanceOfType
//IsNotNull
//IsNull
//IsTrue
//ReplaceNullChars
[TestClass]
public class UnitTest1
{
[TestMethod]
public void _1_3_Test_Divide_Input_Seven_2_Output_Error()
{
//ARRANGE
CalculatorClass calcObj = new CalculatorClass();
string expectedOutput = "Error - Invalid Input";
//ACT
//----HERE WRONG DATA TYPE FOR TESTING----
double result = calcObj.Divide("Seven", 2);
//ASSERT
Assert.AreEqual(expectedOutput, result);
}
}
}
Since your Divide method takes input of double,double the string wrong data type you use cannot be used as input.
In order to allow the input to be string or number, I suggest you to change the argument type to a base class common to both (say, object) and then expand the Divide by trying to parse the data - returning false if the process cannot be finished (or if the exception occur, up to you), similar to TryParse method provided by .Net. You can also extend the out variable to include the error string if you find it suitable.
Also, more appropriate name would be TryDivide than Divide:
namespace Calculator {
public class CalculatorClass {
//METHODS
public bool TryDivide(object num1, object num2, out double doubleVal, out string errorString) {
doubleVal = 0;
errorString = string.Empty;
try {
if (num1 == null || num2 == null) {
errorString = "number(s) cannot be null";
return false;
}
double num = 0, den = 0;
bool parseResult;
if (num1 is double)
num = (double)num1;
else {
parseResult = double.TryParse(num1.ToString(), out num);
if (!parseResult) {
errorString = "numerator cannot be parsed as double";
return false;
}
}
if (num2 is double)
den = (double)num2;
else {
parseResult = double.TryParse(num2.ToString(), out den);
if (!parseResult) {
errorString = "denominator cannot be parsed as double";
return false;
}
}
doubleVal = num / den;
return true;
} catch (Exception ex) {
errorString = ex.ToString();
return false; //may also be changed to throw
}
}
}
}
Just then you will be able call your TryDivide with string input:
double doubleResult;
string errorString;
bool result = calcObj.TryDivide("Seven", 2, out doubleResult, out errorString);
if (!result){ //something is wrong
Console.WriteLine(errorString);
}
You are not able to pass a string where a double parameter is expected. If you absolutely need to be able to pass a string parameter to this method, you should not expect it to fail based on it being an invalid type - only if the conversion to a double failed. In that case, I'd attempt some rudimentary parsing of a string to a double (however in that case you'd probably only want to parse "7", not "seven" - up to you though).
What you have written will never be able to get tested, purely because it will never compile using C#.
If all you want is to test exception handling in unit tests and how you can fail tests by passing wrong arguments, look at this example.
public class Sum
{
//Returns the sum of 2 positive odd integers
//If either of arguments is even, return -1
//If either of arguments is negative, throw exception
public int PositiveSumOddOnly(int a, int b)
{
if(a < 0 || b < 0)
throw new InvalidArgumentException("One or more of your arguments is negative");
if(a%2 == 0 || b%2 == 0)
return -1;
return a + b;
}
}
[TestClass]
public class Sum_Test
{
[TestMethod]
public int PositiveSumOddOnly_ShouldThrowInvalidArgumentExecption(int a, int b)
{
Sum s = new Sum();
try
{
int r = s.PositivesumOddOnly(1,-1);
}
catch(InvalidArgumentException e)
{
Assert.AreEqual("One or more of your arguments is negative", e.Message);
}
}
[TestMethod]
public int PositiveSumOddOnly_ShouldReturnNegativeOne(int a, int b)
{
Sum s = new Sum();
int r = s.PositiveSumOddOnly(1,2);
Assert.AreEqual(r,-1);
}
[TestMethod]
public int PositiveSumOddOnly_ShouldReturnSumOfAandB(int a, int b)
{
Sum s = new Sum();
int r = s.PositiveSumOddOnly(1,1);
Assert.AreEqual(r,2);
}
}
This seems to work:
[TestMethod]
public void _1_3_Test_Divide_Input_Seven_2_Output_Error()
{
//ARRANGE
CalculatorClass calcObj = new CalculatorClass();
string expectedOutput = "Error - Invalid Input";
//ACT
//----CHANGED TO STRING. THEN CAST BACK TO DOUBLE INSIDE METHOD----
string result = calcObj.Divide("Seven", "2");
//ASSERT
Assert.AreEqual(expectedOutput, result);
}
Method:
public string Divide(string num1, string num2)
{
string resultMsg = "";
try
{
double num1Dbl = double.Parse(num1);
double num2Dbl = double.Parse(num2);
double result = num1Dbl / num2Dbl;
resultMsg = result.ToString();
return resultMsg;
}
catch (FormatException error)
{
resultMsg = "Error - Invalid Input";
return resultMsg;
}
}
In moving some code over from my test project to the "real" project, which targets Windows CE, some code got embarrassed and turned red in the IDE, namely "TryParse()".
For the lack of ... a horseshoe, the battle was lost; hopefully, the lack of TryParse() will not cause a healthcare.gov-like eCatastrophe; nevertheless is there a better way to rewrite a TryParse() for a TryParseless parser than:
int recCount = 0;
string s = "42";
try {
recCount = Int32.Parse(s);
}
catch {
MessageBox.Show("That was not an int! Consider this a lint-like hint!");
}
?
Considering s is a string value, you can't cast it to int. If int.TryParse is not available then you can create your own method which would return a bool . Something like:
public static class MyIntConversion
{
public static bool MyTryParse(object parameter, out int value)
{
value = 0;
try
{
value = Convert.ToInt32(parameter);
return true;
}
catch
{
return false;
}
}
}
and then to use it:
int temp;
if (!MyIntConversion.MyTryParse("123", out temp))
{
MessageBox.Show("That was not an int! Consider this a lint-like hint!");
}
int.TryParse internally uses try-catch to do parsing and is implemented in similar manner.
public bool TryParseInt32( this string str, out int result )
{
result = default(int);
try
{
result = Int32.Parse( str );
}
catch
{
return false;
}
return true;
}
usage:
int result;
string str = "1234";
if( str.TryParseInt32( out result ) )
{
}
I'm assuming s is a string. If so, your code won't work. The following code should though:
int recCount = 0;
try {
recCount = Int32.Parse(s);
}
catch {
MessageBox.Show("That was not an int! Consider this a lint-like hint!");
}
You could use a regular expression.
public bool IsNumber(string text)
{
Regex reg = new Regex("^[0-9]+$");
bool onlyNumbers = reg.IsMatch(text);
return onlyNumbers;
}
Is there a way to find out which property of the objects threw the exception. I have a Class with 3 properties. I want to give a message to the user that a particular property in the class is wrong.
public class Numbers
{
public string Num1 { get; set; }
public string Num2 { get; set; }
public string Num3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var numbers = new Numbers() { Num1 = "22", Num2 = "err", Num3 = "33" };
// Call an extension method which tries convert to Int
var num = numbers.Num1.StringToInt();
num = numbers.Num2.StringToInt();
num = numbers.Num3.StringToInt();
Console.WriteLine(num);
Console.ReadLine();
}
}
public static class SampleExtension
{
static StackTrace stackTrace = new StackTrace(true);
// Extension method that converts string to Int
public static int StringToInt(this string number)
{
try
{
// Intentionally used 'Convert' instead of 'TryParse' to raise an exception
return Convert.ToInt32(number);
}
catch (Exception ex)
{
// Show a msg to the user that Numbers.Num2 is wrong. "Input string not in correct format"
var msg = stackTrace.GetFrame(1).GetMethod().ToString();
msg = ex.Message;
msg += ex.StackTrace;
throw;
}
}
}
I'm using an extension method to convert sting to int. And i'm looking for a way to catch the wrong property in the extension method itself. I'm using .Net framework 4.0. Please suggest.
I would use Int32.TryParse instead, then you can explicitly handle the failure to parse.
public static int StringToInt(this string number)
{
try
{
int result;
if (!Int32.TryParse(number, out result))
{
// handle the parse failure
}
return result;
}
}
Why not simply supply all needed data to the method during call? Schematically (you can extend it):
public static int ToInt(string number, string info)
{
try
{
// ...
}
catch(Exception e)
{
MessageBox.Show(info);
}
}
// and usage
string str1 = "123";
int n = ToInt(str1, "Trying to parsing str1");
Note
I was answering this question based on .NET 4.5 because the question had no tag for an specific framework version. I leave the answer here because it may be useful for future visitors using .NET 4.5.
I would like to say that your code sample is very ugly since you could overcome this problem by using int.TryParse, but as I guess you wanted to show a generalized case (bad choice) and you just want to know the caller name of the extension method: check the [CallerMemeberNameAttribute] introduced in the 4.5 version of .NET Framework:
http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx
For example, either in an extension or regular method, do this:
public void Method([CallerMemberName] string callerName)
{
}
And the CLR will set the input parameter with the name of the caller!
public static int StringToInt(this Numbers number,
Expression<Func<Numbers, string>> prop)
{
try
{
return Convert.ToInt32(prop.Compile()(number));
}
catch (Exception ex)
{
var expression = (MemberExpression)prop.Body;
string name = expression.Member.Name;
throw new MissingMemberException(string.Format("Invalid member {0}", name));
}
}
And call it:
var num = numbers.StringToInt(p=>p.Num1);
I am writing a bunch of integration tests for a project. I want to call each individual integration point method wrapped in a try/catch block so that when it fails, I get some sort of feedback to display, rather than just crashing the app. I also want to be able to time how long the calls take, and check return values when needed. So, I have an IntegrationResult class with some basic description, result and time elapsed properties:
class IntegrationResult
{
private StopWatch _watch;
public string Description {get;set;}
public string ResultMessage {get;set;}
public bool TestPassed {get;set;}
public string TimeElapsed {get { return _watch == null ? "0" : _watch.Elapsed.TotalMilliseconds.ToString(); } }
public void Start()
{
_watch = StopWatch.StartNew();
}
public void Stop()
{
_watch.Stop();
}
}
The code I keep writing looks like this:
IntegrationResult result = new IntegrationResult();
result.Description = "T-SQL returns expected results";
try
{
result.Start();
SomeIntegrationPoint("potential arguments"); //This is the line being tested
result.Stop();
//do some check that correct data is present
result.TestPassed = true;
result.ResultMessage = "Pulled 10 correct rows";
}
catch(Exception e)
{
result.TestPassed = false;
result.ResultMessage = String.Format("Error: {0}", e.Message);
}
I would really like to be able to just pass the SomeIntegrationPoint method in as an argument and a delegate or something to check the results, but I can't figure out if that's even possible. Are there any frameworks to handle this type of testing, or do you have any suggestions on how I might simplify the code for better reuse? I'm tired of typing this block ;)
(I'm assuming this is C#, as tagged... though the syntax was not in the question.)
You can do this. Just change your result class to include:
class IntegrationResult
{
string Description { get; set; }
string SuccessResultMessage { get; set; }
string FailResultMessage { get; set; }
public IntegrationResult(string desc, string success, string fail)
{
this.Description = desc;
this.SuccessResultMessage = success;
this.FailResultMessage = fail;
}
public bool ExecuteTest(Func<IntegrationResult, bool> test)
{
bool success = true;
try
{
this.Start();
success = test(this);
this.Stop();
this.ResultMessage = success ?
this.SuccessResultMessage :
this.FailResultMessage;
this.TestPassed = true;
}
catch(Exception e)
{
this.TestPassed = false;
this.ResultMessage = String.Format("Error: {0}", e.Message);
success = false;
}
return success;
}
...
You could then change your code for your tests to:
private void myDoTestMethod(string argumentOne, string argumentTwo)
{
IntegrationResult result = new IntegrationResult(
"T-SQL returns expected results",
"Pulled 10 correct rows",
"Wrong number of rows received");
result.Execute( r=>
{
integrationPoint.call(argumentOne, argumentTwo);
//do some check that correct data is present (return false if not)
return true;
});
}
This can easily be extended to include your timings as well.
Have you looked into AOP? PostSharp looks like a nice place to start. There is also an example on exception handling aspects.