check output in MSTest unit test - c#

I want to capture output sent to standard out and standard error within an MSTest unit test so that I can verify it. I've captured output before when explicitly running a Process, but is there a way to do with [I guess] the MSTest process itself? For example:
[TestMethod]
public void OutputTest()
{
MySnazzyMethod("input", 1, 'c');
string stdOutFromMySnazzyMethod = /* ??? */;
Assert.AreEqual("expected output", stdOutFromMySnazzyMethod);
}

I'm not sure there is a way to grab the output of an already running Process. What you could do though is refactor your code slightly to not write to Console.WriteLine but instead take in a TextWriter instance and write to that.
In production you can then just pass Console.Out to the method. In test code you could mock this type and provide much more accurate testing. For example
[TestMethod]
public void OutputTest()
{
var writer = new Mock<TextWriter>(MockBehavior.Strict);
writer.Setup(x => x.WriteLine("expected output")).Verifiable();
MySnazzyMethod(writer.Object, "input", 1, 'c');
writer.Verify();
}
Production Code
MySnazzyMethod(Console.Out, "input", 1, 'c');

I liked JaredPar's idea but I didn't want to pass in Console.Out and Console.Error to every helper output method I had. However, my output does go through a single class, so I just set a couple static fields in it:
internal static TextWriter _stdOut = Console.Out;
internal static TextWriter _stdErr = Console.Error;
I updated my output methods in the output handler class to make use of these fields. I then updated that project's AssemblyInfo.cs to include:
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyTestProject")]
This way, I can override _stdOut and _stdErr in my test methods, call my method to be tested (which uses my output handling class), and confirm the output I expected.
OutputHandler._stdOut = new StringWriter();
MySnazzyMethod("input", 1, 'c');
OutputHandler._stdOut.Flush();
string expected = "expected output";
string stdout = OutputHandler._stdOut.ToString().Trim(new[] { '\r', '\n' });
Assert.IsFalse(string.IsNullOrEmpty(stdout));
Assert.AreEqual(expected, stdout);

Just add a couple TraceListener in the class initialize of your test classes.

I'd use Moles to redirect the call to Console writes to a lambda method inside your test code.
string result = "";
System.Moles.MConsole.WriteLineString = (s) =>
{ result = s; };
Assert.IsTrue(result == "The string I want",
"Failed to write to console correctly");
See page 16 in this document: Moles Reference Manual.

Related

NSubstitute: Received() does not check string parameter when string comes from resource

I use NSubstitute for my NUnit tests and try to check if a method is called with the right values. Until know everything works fine. I have to localize the texts and so use resource strings for this. Every unit test now fails, that tests for a method to be recieved, where a string parameter contains a new line.
Here is a simplified example:
// Old Version, where the unit test succeed
public void CallWithText(ICallable callable)
{
callable.ShowText("Some text with a new\nline.");
}
// New Version, where the unit test fails
// Text of `Properties.Resources.TextWithNewLine` in the
// Resources.resx is "Some text with a new
// line."
public void CallWithText(ICallable callable)
{
callable.ShowText(Properties.Resources.TextWithNewLine);
}
[Test]
public void CallWithText_WhenCalled_CallsCallable()
{
var caller = new Caller();
var callable = Substitute.For<ICallable>();
caller.CallWithText(callable);
callable.Received(1).ShowText("Some text with a new\nline.");
}
It seems for me, that there is a problem with the new line. Does anybody has a solution, because it is a mess to adapt all the unit tests.
The problem isn't related to NSubstitute but to String itself. The new line symbol isn't just \n. It's environment specific: \r\n on Windows, \n on Unix, \r on early Mac OSes. Please use Shift+Enter to add new lines properly in Resource manager.
You have a couple of ways to use:
Environment.NewLine property which knows the new line symbol for the current environment:
callable.Received(1).ShowText("Some text with a new" + Environment.NewLine + "line.");
Use explicit new line in your expected string:
callable.Received(1).ShowText(#"Some text with a new
line.");
Comparing exact strings in the unit test will add more maintaining work.
In your case new line can be different in different execution environment.
For strings I suggest asserting passed parameter with Contains method. Where you can check for the more important words
[Test]
public void CallWithText_WhenCalled_CallsCallable()
{
var caller = new Caller();
var callable = Substitute.For<ICallable>();
caller.CallWithText(callable);
callable.Received(1).ShowText(Arg.Is<string>(text => text.Contains("Some text")));
}
This indicates the Properties.Resources.TextWithNewLine and "Some text with a new\nline." and different. Without knowing too much about Properties.Resources.TextWithNewLine, I suggest you try change the test to
[Test]
public void CallWithText_WhenCalled_CallsCallable()
{
var caller = new Caller();
var callable = Substitute.For<ICallable>();
caller.CallWithText(callable);
callable.Received(1).ShowText(Arg.Any<string>());
}
Or use the actual string if you really want to assert the content of the string, change to (make sure the resources file is in the test project)
[Test]
public void CallWithText_WhenCalled_CallsCallable()
{
var caller = new Caller();
var callable = Substitute.For<ICallable>();
caller.CallWithText(callable);
callable.Received(1).ShowText(Properties.Resources.TextWithNewLine);
}

Verifying ArgumentException and its message in Nunit , C#

In my test program in Nunit, I want to verify that it's getting the write Argument Exception by verifying the message.
[Test]
public void ArgumentsWorkbookNameException()
{
const string workbookName = "Tester.xls";
var args = new[] { workbookName, "Sheet1", "Source3.csv", "Sheet2", "Source4.csv" };
Assert.Throws(typeof(ArgumentException), delegate { var appargs = new ApplicationArguments(args); }, "Invalid ending parameter of the workbook. Please use .xlsx");
}
After testing this out, this doesn't work when I modified the message in the main program.
int wbLength = args[0].Length;
// Telling the user to type in the correct workbook name file.
if (args[0].Substring(wbLength-5,5)!=".xlsx")
{
throw new ArgumentException(
"Invalid ending parameter of the workbook. Please use .xlsx random random");
}
The unit test still passed, regardless if I changed the message.
How do I do it? Or is there no such things in C#. My colleague said there are options like that in Ruby and RSPEC, but he's not 100% sure on C#.
Use the fluent interface to create assertions:
Assert.That(() => new ApplicationArguments(args),
Throws.TypeOf<ArgumentException>()
.With.Message.EqualTo("Invalid ending parameter of the workbook. Please use .xlsx random random"));
I agree with Jon that "such tests are unnecessarily brittle". However, there are at least two ways to check for exception message:
1: Assert.Throws returns an exception, so you can make an assertion for its message:
var exception = Assert.Throws<ArgumentException>(() => new ApplicationArguments(args));
Assert.AreEqual("Invalid ending parameter of the workbook. Please use .xlsx random random", exception.Message);
2: [HISTORICAL] Before NUnit 3, you could also use ExpectedException attribute. But, take a note that attribute waits for an exception in the whole tested code, not only in code which throws an exception you except. Thus, using this attribute is not recommended.
[Test]
[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Invalid ending parameter of the workbook. Please use .xlsx random random")]
public void ArgumentsWorkbookNameException()
{
const string workbookName = "Tester.xls";
var args = new[] { workbookName, "Sheet1", "Source3.csv", "Sheet2", "Source4.csv" };
new ApplicationArguments(args);
}
You may also use FluentAssertions to do so, e.g.
subject.Invoking(y => y.Foo("Hello"))
.Should().Throw<InvalidOperationException>()
.WithMessage("Hello is not allowed at this moment");
The message parameter in Assert.Throws isn't the expected exception message; it's the error message to include with the assertion failure if the test fails.
I don't believe that NUnit supports testing the exception message out of the box, and I'd argue that such tests are unnecessarily brittle anyway. If you really want to write your own such helper method you can do so, but I personally wouldn't encourage it. (I very rarely specify a test failure message either, unless it's to include some diagnostic information. If a test fails I'm going to look at the test anyway, so the message doesn't add much.)
I would encourage you to use the generic overload instead though, and a lambda expression, for simplicity:
Assert.Throws<ArgumentException>(() => new ApplicationArguments(args));
(If that's your actual code by the way, there are other problems - try passing in new[] { "xyz" } as an argument...)
In .NET Core 3.1 MSTest project, this is how I did it.
[TestMethod]
public async Task SaveItemAsync_NameIsNull_ThrowsException()
{
var item = new Item
{
Name = null
};
var result = await Assert.ThrowsExceptionAsync<ArgumentException>(() => _service.SaveItemAsync(item));
Assert.AreEqual("The item's name must be set.", result.Message);
}

What is a good approach to get rid of dependency on a StreamReader/FileStream for Unit Tests?

Here's the scenario:
I have a method that reads in a file via a FileStream and a StreamReader in .NET. I would like to unit test this method and somehow remove the dependency on the StreamReader object.
Ideally I would like to be able to supply my own string of test data instead of using a real file. Right now the method makes use of the StreamReader.ReadLine method throughout. What is an approach to modifying the design I have now in order to make this test possible?
Depend on Stream and TextReader instead. Then your unit tests can use MemoryStream and StringReader. (Or load resources from inside your test assembly if necessary.)
Note how ReadLine is originally declared by TextReader, not StreamReader.
The simplest solution would be to have the method accept a Stream as a parameter instead of opening its own FileStream. Your actual code could pass in the FileStream as usual, while your test method could either use a different FileStream for test data, or a MemoryStream filled up with what you wanted to test (that wouldn't require a file).
Off the top of my head, I'd say this is a great opportunity to investigate the merits of Dependency Injection.
You might want to consider redesigning your method so that it takes a delegate that returns the file's contents. One delegate (the production one) might use the classes in System.IO, while the second one (for unit testing), returns the contents directly as a string.
I think the idea is to dependency inject the TextReader and mock it for unit testing. I think you can only mock the TextReader because it is an abstract class.
public class FileParser
{
private readonly TextReader _textReader;
public FileParser(TextReader reader)
{
_textReader = reader;
}
public List<TradeInfo> ProcessFile()
{
var rows = _textReader.ReadLine().Split(new[] { ',' }).Take(4);
return FeedMapper(rows.ToList());
}
private List<TradeInfo> FeedMapper(List<String> rows)
{
var row = rows.Take(4).ToList();
var trades = new List<TradeInfo>();
trades.Add(new TradeInfo { TradeId = row[0], FutureValue = Convert.ToInt32(row[1]), NotionalValue = Convert.ToInt32(row[3]), PresentValue = Convert.ToInt32(row[2]) });
return trades;
}
}
and then Mock using Rhino Mock
public class UnitTest1
{
[Test]
public void Test_Extract_First_Row_Mocked()
{
//Arrange
List<TradeInfo> listExpected = new List<TradeInfo>();
var tradeInfo = new TradeInfo() { TradeId = "0453", FutureValue = 2000000, PresentValue = 3000000, NotionalValue = 400000 };
listExpected.Add(tradeInfo);
var textReader = MockRepository.GenerateMock<TextReader>();
textReader.Expect(tr => tr.ReadLine()).Return("0453, 2000000, 3000000, 400000");
var fileParser = new FileParser(textReader);
var list = fileParser.ProcessFile();
listExpected.ShouldAllBeEquivalentTo(list);
}
}
BUT the question lies in the fact whether it is a good practice to pass such an object from the client code rather I feel it should be managed with using within the class responsible for processing. I agree with the idea of using a sep delegate for the actual code and one for the unit testing but again that is a bit of extra code in production. I may be a bit lost with the idea of dependency injection and mocking to even file IO open/read which actually is not a candidate for unit testing but the file processing logic is which can be tested by just passing the string content of the file (AAA23^YKL890^300000^TTRFGYUBARC).
Any ideas please! Thanks

How can I use NUnit to test a method with out or ref parameters?

If I have a function which accepts an out parameter and accepts an input form console -
public void Test(out int a)
{
a = Convert.ToInt16(Console.ReadLine());
}
How can I accept an input using Console.Readline() during NUnit test? How can I use NUnit to test this method?
I tried using this code for my NUnit test case -
[TestCase]
public void test()
{
int a = 0;
ClassAdd ad = new ClassAdd();
ad.addition(out a);
//a should be equal to the value I input through console.Readline()
Assert.AreEqual(<some value I input>, a, "test");
}
how can I test a method which accepts an out parameter and also accepts an user input from Console?
You can use the SetIn method of System.Console to set the the input source:
StringReader reader = new StringReader("some value I input" + Enivronment.NewLine);
Console.SetIn(reader);
int a = 0;
ClassAdd ad = new ClassAdd();
ad.addition(out a);
Assert.AreEqual(<some value I input>, a, "test");
EDIT: To test multiple values, just separate each input with a new line:
string[] lines = new[] { "line1", "line2" };
StringReader input = new StringReader(String.Join(Environment.NewLine, lines));
Console.SetIn(input);
string input1 = Console.ReadLine(); //will return 'line1'
string input2 = Console.ReadLine(); //will return 'line2'
There are 2 slightly different issues combined here.
You want to test a method that returns a value in an out parameter. This is actually quite trivial and is hardly different from a method that returns its value as a normal function.
You want to test a method that reads input form the console. This one is quite a bit trickier, and goes a little into the design of the object you're trying to test. The problem is that the "Console" is a global object, and that immediately makes it more difficult to test.
The craziest thing to note is that you want to test a method that takes input from the console. I.e. this implies user interaction. This is hardly the way to go about "automated testing" don't you think?
In answer to #2, I suggest you look at some of Misko Hevery's articles and videos on writing testable code.
http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/
For a brief summary specfic to your problem:
MethodToTest currently wants input from the console.
The class that holds MethodToTest should take an "input stream" in its constructor. (This is a technique called dependency injection.)
In production code, your class will be created with the normal "Console" as its input stream.
In test code, the class will be constructed with a Mock input stream that will feed values controlled by the test.
In this way your test can be automated, and well controlled in terms of inputs; and therefore expected outputs.
Bare Bones Sample Code
[TestCase]
public void test()
{
<use appropriate type here> MockInputStream = new ...;
ClassToTest testClass = new ClassToTest(MockInputStream);
int Actual = 0;
MockInputStream.PutNextInput("4");
ClassToTest.MethodToTest(out Actual);
Assert.AreEqual(4, Actual, "MockInputStream placed 4 as the next value to read");
}

Feeding multiple pairs of files into a test method

I have a test method that tests the ToString() method of a class against known good outputs.
/// <summary>
///A test for ToString
///</summary>
[TestMethod()]
public void ToStringTest()
{
string input = System.IO.File.ReadAllText(#"c:\temp\input2005.txt");
MyClass target = new MyClass(input);
string expected = System.IO.File.ReadAllText(#"c:\temp\output2005.txt");
string actual;
actual = target.ToString();
Assert.AreEqual(expected, actual);
}
The method works great, but I already have several pairs of input/output files. Experience tells me that I don't want to write a separate test method for each pair. I also don't want to loop through each pair of files because I won't know which pair caused the test to fail. What do I do?
You could use a loop inside your test but you will only get one pass or failure for all of them. Some test frameworks will generate and run a separate test for each set of inputs specified like this:
[Test]
[Row(#"c:\temp\input2005.txt", #"c:\temp\output2005.txt")]
[Row(#"c:\temp\input2006.txt", #"c:\temp\output2006.txt")]
[Row(#"c:\temp\input2007.txt", #"c:\temp\output2007.txt")]
public void ToStringTest(string inputPath, string expectedPath)
{
string input = System.IO.File.ReadAllText(inputPath);
MyClass target = new MyClass(input);
string expected = System.IO.File.ReadAllText(expectedPath);
string actual;
actual = target.ToString();
Assert.AreEqual(expected, actual);
}
The above will work with MbUnit but I believe many of the other frameworks also support similar features.
As an aside, unit tests shouldn't really touch the file system as you can get test failures due to external factors (such as a file being locked) which makes your tests unreliable.
If you wish to test all pairs of files, you could put the file that failed as a message to the assertion:
foreach(file in filenames)
{
/* run your test */
Assert.AreEqual(expected, actual, "Failure occured on file: " + file);
}
This would print out a message telling you which file the failure occured on.
Additionally, you could specify your strings inside of the test itself instead of putting them in extrenal files. I'm not sure how your tests are setup but if you did that you wouldn't have to worry about the external depencies of the file paths:
MyClass target = new MyClass("myTestString");
string actual = target.ToString();
string expected = "MyExpectedString";
Assert.AreEqual(expected, actual);
This would keep all of your test data together.
The MbUnit framework has the concept of row tests and I believe there is an NUnit Add-In for 2.4 that provides similar functionality for NUnit. Using MbUnit syntax it would look something like this:
/// <summary>
///A test for ToString
///</summary>
[Test]
[RowTest(#"c:\temp\input2005.txt", #"c:\temp\output2005.txt")]
[RowTest(#"c:\temp\input2006.txt", #"c:\temp\output2006.txt")]
[RowTest(#"c:\temp\input2007.txt", #"c:\temp\output2007.txt")]
public void ToStringTest(string inputFile, string outputFile)
{
string input = System.IO.File.ReadAllText(inputFile);
MyClass target = new MyClass(input);
string expected = System.IO.File.ReadAllText(outputFile);
string actual;
actual = target.ToString();
Assert.AreEqual(expected, actual);
}

Categories