Friday, March 05, 2010

The case of the missing assert

I was looking at a friend of mines code late last night and he was writing his tests in MSTest. The test he wrote was an expected exception test.

        [TestMethod]
        [ExpectedException(typeof(SomeException))]
        public void CanDoSomething_WithSomeThingThatThrows_ReturnsTrue()
        {
            var something = SomeFactory.GetSomething();
            something.DoSomething(x => { throw new NotImplementedException(); });

            var result = something.DoSomethingElse<ISomeThingElse>();

            Assert.IsTrue(true);
        }
I was tired, it was late, I'm used to a using different test framework but I got in contact straight away.
....spotted something in your codez. You're Assert is true everytime.
Of course dear reader you will have spotted the ExpectedException attribute as you, unlike me are not an idiot but my eyes see:
        [TEST BLAH]
        [yes i didn't spot the attrib here]
        public void STUFF_I_WILL_READ_IF_TEST_IS_NOT_OBVIOUS()
        {
            //SOME ARRANGING STUFF
            ..blah
            //THIS IS IMPORTANT
            var result = something.DoSomethingElse<ISomeThingElse>();
            //AND SO IS THIS
            Assert.IsTrue(true);
        }
In my semi-comatosed state my eyes fix on Assert.IsTrue(true) and I can look away from it. I'm used to NUnit's (and xUnit and mbUnit) Assert.Throws
        [TEST BLAH]
        public void STUFF_I_WILL_READ_IF_TEST_IS_NOT_OBVIOUS()
        {
            //SOME ARRANGING STUFF
            ..blah
            //THIS IS IMPORTANT
            Assert.Throws(()=> something.DoSomethingElse<ISomeThingElse>());

        }
However some frameworks don't support this. So you've got 2 options.
Option 1 Attribute and fail.
        [TestMethod]
        [ExpectedException(typeof(SomeException))]
        public void CanDoSomething_WithSomeThingThatThrows_ReturnsTrue()
        {
            var something = SomeFactory.GetSomething();
            something.DoSomething(x => { throw new NotImplementedException(); });

            var result = something.DoSomethingElse<ISomeThingElse>();

            Assert.Fail();
        }
As my eyes always look the assert and so if I see a fail. I'll look to see why.
Option 2 No Attribute try catch
        [TestMethod]
        public void CanDoSomething_WithSomeThingThatThrows_ReturnsTrue()
        {
            var something = SomeFactory.GetSomething();
            something.DoSomething(x => { throw new NotImplementedException(); });
            try
            {
                var result = something.DoSomethingElse<ISomeThingElse>();
                Assert.Fail();
            }
            catch (SomeException)
            {
                Assert.IsTrue(true);
            } 
            catch (Exception)
            {
               Assert.Fail();
            }
        }
And what happens when we want to invoke an action that doesn't expect anything. That you just want to run to see if doesn't fail. Well you've got Assert.DoesNotThrow in most .Net unit testing frameworks.
You can call the Assert.IsTrue(True);
        [TestMethod]
        public void CanDoSomething_WithSomeThingThatDoesnotThrow()
        {
            var something = SomeFactory.GetSomething();
            something.DoSomething(x => { throw new NotImplementedException(); });
           Assert.IsTrue(True);
        }
Or just drop the assert.
        [TestMethod]
        public void CanDoSomething_WithSomeThingThatDoesnotThrow()
        {
            var something = SomeFactory.GetSomething();
            something.DoSomething(x => { throw new NotImplementedException(); });
          
        }
So with MStest you can't use Assert.Throws but you do have options. Mine is to use another framework ;)

1 comment:

Will said...

Before I discovered the NUnit Assert.Throws syntax I used to use ExpectedException and have an Assert.IsTrue(false) at the end of the test. But I always put a message in there to explain why it was fail.

It never felt right to just not have an assert but at the same time that didn't look quite right either.

One problem I have with testing constructors throw via Assert.Throws is that you have to have curly brackets in your lambda EG

Assert.Throws(() => { var myClass = new myClass(null)} );

It just doesn't sit right with me