Being Cellfish

Stuff I wished I've found in some blog (and sometimes did)

December, 2008

Change of Address
This blog has moved to blog.cellfish.se.
Posts
  • Being Cellfish

    2008 Advent Calendar December 22nd

    • 0 Comments
    
    
    1: namespace Advent22 2: { 3: public class FileUtil_Specification 4: { 5: private FileUtilWithDelete Given_A_Readable_File(string content) 6: { 7: FileUtilWithDelete file = new FileUtilWithDelete("SomeFile.txt"); 8: file.Create(content); 9: return file; 10: } 11:   12: private FileUtilWithDelete Given_An_Unreadable_File() 13: { 14: FileUtilWithDelete file = Given_A_Readable_File("SHOULD NOT BE ABLE TO READ THIS"); 15: file.Readable = false; 16: return file; 17: } 18:   19: [Fact] 20: public void Reading_A_Readable_File_Returns_File_Content() 21: { 22: using (FileUtilWithDelete file = Given_A_Readable_File("CONTENT")) 23: { 24: string content = file.Read(); 25: Assert.Equal<string>("CONTENT", content); 26: } 27: } 28:   29: [Fact] 30: public void Reading_An_Unreadable_File_Throws_Correct_Exception() 31: { 32: using (FileUtilWithDelete file = Given_An_Unreadable_File()) 33: { 34: Assert.Throws<AccessViolationException>(() => { file.Read(); }); 35: } 36: } 37: } 38: }

    So now we have a better name for our specification. But why should everything that has to do with FileUtil be in the same specification?

  • Being Cellfish

    2008 Advent Calendar December 21st

    • 0 Comments
    
    
    1: public class Advent21 2: { 3: private FileUtilWithDelete Given_A_Readable_File(string content) 4: { 5: FileUtilWithDelete file = new FileUtilWithDelete("SomeFile.txt"); 6: file.Create(content); 7: return file; 8: } 9:   10: private FileUtilWithDelete Given_An_Unreadable_File() 11: { 12: FileUtilWithDelete file = Given_A_Readable_File("SHOULD NOT BE ABLE TO READ THIS"); 13: file.Readable = false; 14: return file; 15: } 16:   17: [Fact] 18: public void Reading_A_Readable_File_Returns_File_Content() 19: { 20: using (FileUtilWithDelete file = Given_A_Readable_File("CONTENT")) 21: { 22: string content = file.Read(); 23: Assert.Equal<string>("CONTENT", content); 24: } 25: } 26:   27: [Fact] 28: public void Reading_An_Unreadable_File_Throws_Correct_Exception() 29: { 30: using (FileUtilWithDelete file = Given_An_Unreadable_File()) 31: { 32: Assert.Throws<AccessViolationException>(() => { file.Read(); }); 33: } 34: } 35: }

    Now let's consider the name of the class containing all tests. A pretty bad name if you ask me. And since we've used the "given" pattern from BDD we can use BDD to inspire us into a better "test class name".

  • Being Cellfish

    The 2008 Advent Calendar wrap up

    • 3 Comments

    Writing all these tests I sometimes felt that the changes between two days was not significant. And sometimes I felt there were versions that did not make it into this advent calendar. But I stuck with the versions you have seen since I think they represent a quite natural evolution of the initial test and hence I have not presented any side tracks that eventually turn into a dead end because I wanted to progress toward the final version which is the one I like most of all these versions. Also I had to be a little bit puristic and let even the smallest changes result in a version by it self rather than merging a number of small changes into a single version change in order get 24 different versions. There are however a few of the versions that I think are worth a special mentioning.

    • December 3rd: I think this is a typical example of how people write their tests when they're starting out to use TDD. In my experience TDD novices tend to put too much into each test.
    • December 5th: The second most common mistake is to forget that standard software engineering practices and common sense still apply to the test code. They tend to forget code reuse in their tests.
    • December 11th: I think this is the first acceptable version in this series. Simple, easy to understand tests with code reuse. TDD novices that take TDD seriously typically end up with this kind of tests pretty soon.
    • December 15th: The use of an object that extends the object under test and adds functionality needed to simplify testing is a great step forward toward writing good tests (IMHO). I think this version of the test basically is the first peek at BDD-type test-writing.
    • December 20th: Now we have easy to understand tests and each method have really descriptive names that are easy to read. I think this is the first really good version of the test in this series.
    • December 24th: In my experience people tend to put many different kinds of tests into the same test class ending up with tear-down code that has to take take the state of several tests into account. This makes the test class more complex than it has to be. The use of several specialized test classes for different types of test scenarios is definitely something you start thinking of after writing several unit tests. I think it is a common misconception that you should have one test class for each production class. Some test classes will test several production classes while others production classes will need several test classes to complete the test suite.

    Please leave a comment and let me know what your favorite version in this series of test is!

  • Being Cellfish

    2008 Advent Calendar December 20th

    • 0 Comments
    
    
    1: public class Advent20 2: { 3: private FileUtilWithDelete Given_A_File(string content, bool readable) 4: { 5: FileUtilWithDelete file = new FileUtilWithDelete("SomeFile.txt"); 6: file.Create(content); 7: file.Readable = readable; 8: return file; 9: } 10:   11: [Fact] 12: public void Reading_A_Readable_File_Returns_File_Content() 13: { 14: using (FileUtilWithDelete file = Given_A_File("CONTENT", true)) 15: { 16: string content = file.Read(); 17: Assert.Equal<string>("CONTENT", content); 18: } 19: } 20:   21: [Fact] 22: public void Reading_An_Unreadable_File_Throws_Correct_Exception() 23: { 24: using (FileUtilWithDelete file = Given_A_File("SHOULD NOT BE ABLE TO READ THIS", false)) 25: { 26: Assert.Throws<AccessViolationException>(() => { file.Read(); }); 27: } 28: } 29: }

    The "Given_By" pattern has been "stolen" from BDD and the whole purpose is to make the test as readable as possible. Reading a test should more or less be like reading a paragraph. So "Given_A_File" is not really that great. It is probably better to have different Given-methods depending on what we want.

  • Being Cellfish

    2008 Advent Calendar December 15th

    • 0 Comments
    
    
    1: public class Advent15 2: { 3: private FileUtilWithDelete SetUpReadable(string content) 4: { 5: FileUtilWithDelete file = new FileUtilWithDelete("SomeFile.txt"); 6: file.Create(content); 7: return file; 8: } 9:   10: private FileUtilWithDelete SetUpUnreadable(string content) 11: { 12: FileUtilWithDelete file = SetUpReadable(content); 13: file.Readable = false; 14: return file; 15: } 16:   17: [Fact] 18: public void TestReadReadableFile() 19: { 20: using (FileUtilWithDelete file = SetUpReadable("CONTENT")) 21: { 22: string content = file.Read(); 23: Assert.Equal<string>("CONTENT", content); 24: } 25: } 26:   27: [Fact] 28: public void TestReadUnreadableFile() 29: { 30: using (FileUtilWithDelete file = SetUpUnreadable("SHOULD NOT BE ABLE TO READ THIS")) 31: { 32: Assert.Throws<AccessViolationException>(() => { file.Read(); }); 33: } 34: } 35: }

    The two setup methods does not really feel necessary. Why not add another parameter to it and control readability that way?

  • Being Cellfish

    2008 Advent Calendar December 5th

    • 2 Comments
    
    
    1: public class Advent5 : IDisposable 2: { 3: private IFileUtil m_file; 4:   5: public void Dispose() 6: { 7: m_file.Delete(); 8: } 9:   10: [Fact] 11: public void TestReadOK() 12: { 13: // Create a test file 14: m_file = new FileUtil("SomeFile.txt"); 15: m_file.Create("CONTENT"); 16:   17: // Should be able to read file 18: Assert.DoesNotThrow(() => { m_file.Read(); }); 19: } 20:   21: [Fact] 22: public void TestReadFails() 23: { 24: // Create a test file 25: m_file = new FileUtil("SomeFile.txt"); 26: m_file.Create("CONTENT"); 27:   28: m_file.Readable = false; 29:   30: // Should NOT be able to read file. 31: Assert.Throws<AccessViolationException>(() => { m_file.Read(); }); 32: } 33: }

    Now we're using Xunit-net's clean up mechanism, the IDisposableinterface. We still got some redundant code in the setup part of each test. Next I'll refactor that.

  • Being Cellfish

    2008 Advent Calendar December 24th

    • 0 Comments
    
    
    1: namespace Advent24 2: { 3: public class FileUtil_Tests 4: { 5: public FileUtilWithDelete Given_A_Readable_File(string content) 6: { 7: FileUtilWithDelete file = new FileUtilWithDelete("SomeFile.txt"); 8: file.Create(content); 9: return file; 10: } 11: } 12:   13: public class FileUtil_Tests_With_Readable_File : FileUtil_Tests 14: { 15: [Fact] 16: public void Reading_A_Readable_File_Returns_File_Content() 17: { 18: using (FileUtilWithDelete file = Given_A_Readable_File("CONTENT")) 19: { 20: string content = file.Read(); 21: Assert.Equal<string>("CONTENT", content); 22: } 23: } 24: } 25:   26: public class FileUtil_Tests_With_Unreadable_File : FileUtil_Tests 27: { 28: private FileUtilWithDelete Given_An_Unreadable_File() 29: { 30: FileUtilWithDelete file = Given_A_Readable_File("SHOULD NOT BE ABLE TO READ THIS"); 31: file.Readable = false; 32: return file; 33: } 34:   35: [Fact] 36: public void Reading_An_Unreadable_File_Throws_Correct_Exception() 37: { 38: using (FileUtilWithDelete file = Given_An_Unreadable_File()) 39: { 40: Assert.Throws<AccessViolationException>(() => { file.Read(); }); 41: } 42: } 43: } 44: }

    This is the final version of this test. Tomorrow you'll get my final thoughts on this advent calendar.

  • Being Cellfish

    2008 Advent Calendar December 23rd

    • 0 Comments
    
    
    1: namespace Advent23 2: { 3: public class FileUtil_Tests_With_Readable_File 4: { 5: private FileUtilWithDelete Given_A_Readable_File(string content) 6: { 7: FileUtilWithDelete file = new FileUtilWithDelete("SomeFile.txt"); 8: file.Create(content); 9: return file; 10: } 11:   12: [Fact] 13: public void Reading_A_Readable_File_Returns_File_Content() 14: { 15: using (FileUtilWithDelete file = Given_A_Readable_File("CONTENT")) 16: { 17: string content = file.Read(); 18: Assert.Equal<string>("CONTENT", content); 19: } 20: } 21: } 22:   23: public class FileUtil_Tests_With_Unreadable_File 24: { 25: private FileUtilWithDelete Given_An_Unreadable_File() 26: { 27: FileUtilWithDelete file = new FileUtilWithDelete("SomeFile.txt"); 28: file.Create("SHOULD NOT BE ABLE TO READ THIS"); 29: file.Readable = false; 30: return file; 31: } 32:   33: [Fact] 34: public void Reading_An_Unreadable_File_Throws_Correct_Exception() 35: { 36: using (FileUtilWithDelete file = Given_An_Unreadable_File()) 37: { 38: Assert.Throws<AccessViolationException>(() => { file.Read(); }); 39: } 40: } 41: } 42: }

    Once again we've ended up with the same code in different places. That should be fixed.

  • Being Cellfish

    2008 Advent Calendar December 13th

    • 0 Comments
    
    
    1: public class Advent13 : IDisposable 2: { 3: private IFileUtil m_file; 4:   5: private void SetUpReadable(string content) 6: { 7: m_file = new FileUtil("SomeFile.txt"); 8: m_file.Create(content); 9: } 10:   11: private void SetUpUnreadable(string content) 12: { 13: SetUpReadable(content); 14: m_file.Readable = false; 15: } 16:   17: public void Dispose() 18: { 19: m_file.Delete(); 20: } 21:   22: [Fact] 23: public void TestReadReadableFile() 24: { 25: SetUpReadable("CONTENT"); 26: string content = m_file.Read(); 27: Assert.Equal<string>("CONTENT", content); 28: } 29:   30: [Fact] 31: public void TestReadUnreadableFile() 32: { 33: SetUpUnreadable("SHOULD NOT BE ABLE TO READ THIS"); 34: Assert.Throws<AccessViolationException>(() => { m_file.Read(); }); 35: } 36: }

    So far we've used the unit test framework clean up mechanism to remove the test file after each test. I don't see anything wrong with that but another way to do it would be to introduce a new class that does this for us. Tomorrow I'll make use of this new class:

        public class FileUtilWithDelete : FileUtil, IDisposable
        {
            public FileUtilWithDelete(string path)
                : base(path)
            {
                
            }
    
            public void Dispose()
            {
                Delete();
            }
        }
  • Being Cellfish

    2008 Advent Calendar December 12th

    • 2 Comments
    
    
    1: public class Advent12 : IDisposable 2: { 3: private IFileUtil m_file; 4:   5: private void SetUp(string content) 6: { 7: m_file = new FileUtil("SomeFile.txt"); 8: m_file.Create(content); 9: } 10:   11: public void Dispose() 12: { 13: m_file.Delete(); 14: } 15:   16: [Fact] 17: public void TestReadReadableFile() 18: { 19: SetUp("CONTENT"); 20: string content = m_file.Read(); 21: Assert.Equal<string>("CONTENT", content); 22: } 23:   24: [Fact] 25: public void TestReadUnreadableFile() 26: { 27: SetUp("SHOULD NOT BE ABLE TO READ THIS"); 28: m_file.Readable = false; 29: Assert.Throws<AccessViolationException>(() => { m_file.Read(); }); 30: } 31: }

    Since the setup-method is called explicitly, why not have several setup-methods for different kinds of setup?

Page 1 of 3 (25 items) 123