2
\$\begingroup\$

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.

asked Oct 17, 2017 at 17:43
\$\endgroup\$

1 Answer 1

-1
\$\begingroup\$

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);
 }
}
answered Mar 15, 2018 at 16:48
\$\endgroup\$
1
  • 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\$ Commented Apr 14, 2018 at 18:37

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.