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

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);
}

Related

Is it possible to build your TestCaseSource list inside SetUp using NUnit?

Can I build my TestCaseData list in my SetUp? Because with this setup my test is just being skipped. Other regular tests are running just fine.
[TestFixture]
public class DirectReader
{
private XDocument document;
private DirectUblReader directReader;
private static UblReaderResult result;
private static List<TestCaseData> rootElementsTypesData = new List<TestCaseData>();
[SetUp]
public void Setup()
{
var fileStream = ResourceReader.GetScenario("RequiredElements_2_1.xml");
document = XDocument.Load(fileStream);
directReader = new DirectUblReader();
result = directReader.Read(document);
// Is this allowed?
rootElementsTypesData.Add(new TestCaseData(result.Invoice.Id, new IdentifierType()));
rootElementsTypesData.Add(new TestCaseData(result.Invoice.IssueDate, new IdentifierType()));
}
[Test, TestCaseSource(nameof(rootElementsTypesData))]
public void Expects_TypeOfObject_ToBeTheSameAs_InputValue(object inputValue, object expectedTypeObject)
{
Assert.That(inputValue, Is.TypeOf(expectedTypeObject.GetType()));
}
}
As stated by #IMil, the answer is No... that's not possible.
TestCaseSource is used by NUnit to build a list of the tests to be run. It associates a method with a particular set of arguments. NUnit then creates an internal representation of all your tests.
OTOH SetUp (and even OneTimeSetUp is used when those tests are being run. By that time, the number of tests and the actual arguments to each of them are fixed nothing can change them.
So, in order to do what you seem to want to do, your TestCaseSource has to stand on it's own, fully identifying the arguments to be used for the test. That's why NUnit gives you the capability of making the source a method or property, rather than just a simple list.
In your case, I suggest something like...
private static IEnumerable<TestCaseData> RootElementsTypesData()
{
var fileStream = ResourceReader.GetScenario("RequiredElements_2_1.xml");
document = XDocument.Load(fileStream);
directReader = new DirectUblReader();
result = directReader.Read(document);
yield return new TestCaseData(result.Invoice.Id, new IdentifierType()));
yield return new TestCaseData(result.Invoice.IssueDate, new IdentifierType()));
}
Obviously, this is only "forum code" and you'll have to work with it to get something that actually compiles and works for your case.
No, this is impossible.
Methods decorated with [SetUp] are run before each test case.
This means NUnit will first build list of test cases, then run Setup() before each of them.
Therefore, your Setup() never gets called, and list of test cases remains empty.

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);
}

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");
}

check output in MSTest unit test

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.

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