I want to properly test the strong exception guarantee of a method.
The method in question is SetFiles
. It does not have side-effects (it only modifies the object it is called upon). It is expected to change some object fields and to modify the elements of FileList
field - a collection. In case the argument is invalid then it throws ArgumentException
and it is expected to not modify the object.
This is what I have:
[TestClass]
public class UnitTest_Files
{
[TestMethod]
public void TestFiles1()
{
var fs = new Files();
// 1st call to SetFiles. valid
fs.SetFiles(new string[]
{
@"C:\work\file1.txt",
@"C:\work\file2.avi",
@"C:\work\file3",
});
Assert.AreEqual(@"C:\work", fs.ParentFolder);
Assert.AreEqual(3, fs.FileList.Count);
Assert.AreEqual(@"C:\work\file1.txt", fs.FileList[0].Path);
Assert.AreEqual(@"C:\work\file2.avi", fs.FileList[1].Path);
Assert.AreEqual(@"C:\work\file3", fs.FileList[2].Path);
// 2nd call to SetFiles. valid
fs.SetFiles(new string[]
{
@"C:\muzică\Diabulus in Musica\poze\membri\Zuberoa Aznárez.jpg",
@"C:\muzică\Diabulus in Musica\poze\membri\Gorka Elso.jpg",
});
Assert.AreEqual(@"C:\muzică\Diabulus in Musica\poze\membri", fs.ParentFolder);
Assert.AreEqual(2, fs.FileList.Count);
Assert.AreEqual(@"C:\muzică\Diabulus in Musica\poze\membri\Zuberoa Aznárez.jpg", fs.FileList[0].Path);
Assert.AreEqual(@"C:\muzică\Diabulus in Musica\poze\membri\Gorka Elso.jpg", fs.FileList[1].Path);
// 3rd call to SetFiles. Invalid
// set to invalid structure (files not in same folder)
// and expect throw
// and no change
try
{
fs.SetFiles(new string[]
{
@"C:\muzică\folder1\file2.txt",
@"C:\muzică\folder2\file2.txt"
});
Assert.Fail("expected exception");
}
catch
{
}
// test that fs was not modified
Assert.AreEqual(@"C:\muzică\Diabulus in Musica\poze\membri", fs.ParentFolder);
Assert.AreEqual(2, fs.FileList.Count);
Assert.AreEqual(@"C:\muzică\Diabulus in Musica\poze\membri\Zuberoa Aznárez.jpg", fs.FileList[0].Path);
Assert.AreEqual(@"C:\muzică\Diabulus in Musica\poze\membri\Gorka Elso.jpg", fs.FileList[1].Path);
}
}
This is my first dive into C# unit testing and the code I wrote seems very cumbersome. I particularly don't like the copy-pasted last tests. The object is not clonable/copyable and I don't have any other reason to implement that. So I don't know how other way to test that object after is the same as before. Also the entire test doesn't seem very scalable.
1 Answer 1
Do not attempt to put all the test criteria in one test. Make seperate (small) tests, which each cover one specific scenario.
If all tests pass, you know it is ok. But make sure the tests do not depend on each other: that they can run in any order.
For the exception assert, it is better to use an Attribute on the test method:
Update:
As Roland Illig mentioned, it is good to check that nothing was modified.
Updated code fragment:
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void SetFiles_FilesNotInTheSameFolder_ExpectedException()
{
// 3rd call to SetFiles. Invalid
// set to invalid structure (files not in same folder)
// and expect throw
// and no change
var validParentFolder = @"c:\C:\muzică\folder1";
var validFile1 = @"c:\C:\muzică\folder1\file1.txt";
var invalidFile1 = @"C:\muzică\folder1\file2.txt";
var invalidFile2 = @"C:\muzică\folder2\file2.txt";
var fs = new Files();
fs.SetFiles(new string[]
{
validFile1
});
fs.SetFiles(new string[]
{
invalidFile1,
invalidFile2
});
// test that fs was not modified
Assert.AreEqual(1, fs.FileList.Count);
Assert.AreEqual(validParentFolder, fs.ParentFolder);
Assert.AreEqual(validFile1, fs.FileList[0].Path);
}
Last remark, if this is working with real files, try to use stubs or mocks.
Update:
Below is compete test file, with the separated tests
[TestClass]
public class UnitTest_Files
{
private string expectedFile1;
[TestMethod]
public void SetFiles_Valid()
{
var fs = new Files();
// 1st call to SetFiles. valid
var expectedParentFolder = @"C:\work";
var expectedFile1 = $@"{expectedParentFolder}\file1.txt";
var expectedFile2 = $@"{expectedParentFolder}\file2.avi";
var expectedFile3 = $@"{expectedParentFolder}\file3";
fs.SetFiles(new string[]
{
expectedFile1,
expectedFile2,
expectedFile3
});
Assert.AreEqual(3, fs.FileList.Count);
Assert.AreEqual(@"C:\work", fs.ParentFolder);
Assert.AreEqual(expectedFile1, fs.FileList[0].Path);
Assert.AreEqual(expectedFile2, fs.FileList[1].Path);
Assert.AreEqual(expectedFile3, fs.FileList[2].Path);
}
[TestMethod]
public void SetFiles_Specialchars_Valid()
{
// 2nd call to SetFiles. valid
var expectedParentFolder = @"C:\muzică\Diabulus in Musica\poze\membri";
var expectedfile1 = $@"{expectedParentFolder}\Zuberoa Aznárez.jpg";
var expectedfile2 = $@"{expectedParentFolder}\Gorka Elso.jpg";
var fs = new Files();
fs.SetFiles(new string[]
{
expectedfile1,
expectedfile2
});
Assert.AreEqual(expectedParentFolder, fs.ParentFolder);
Assert.AreEqual(2, fs.FileList.Count);
Assert.AreEqual(expectedFile1, fs.FileList[0].Path);
Assert.AreEqual(expectedFile2, fs.FileList[1].Path);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void SetFiles_FilesNotInTheSameFolder_ExpectedException()
{
// 3rd call to SetFiles. Invalid
// set to invalid structure (files not in same folder)
// and expect throw
// and no change
var validParentFolder = @"c:\C:\muzică\folder1";
var validFile1 = @"c:\C:\muzică\folder1\file1.txt";
var invalidFile1 = @"C:\muzică\folder1\file2.txt";
var invalidFile2 = @"C:\muzică\folder2\file2.txt";
var fs = new Files();
fs.SetFiles(new string[]
{
validFile1
});
fs.SetFiles(new string[]
{
invalidFile1,
invalidFile2
});
// test that fs was not modified
Assert.AreEqual(1, fs.FileList.Count);
Assert.AreEqual(validParentFolder, fs.ParentFolder);
Assert.AreEqual(validFile1, fs.FileList[0].Path);
}
}
-
1\$\begingroup\$ The code from this answer does not explain how to check that in the exception case, the object state is not modified at all. That is an important part of the original question. \$\endgroup\$Roland Illig– Roland Illig2018年04月14日 18:37:06 +00:00Commented Apr 14, 2018 at 18:37
Explore related questions
See similar questions with these tags.