cfs
cfs

Reputation: 1324

Right way of testing for Exceptions

I do TDD on a project with 80% simple logic and 20% complex logic. I find myself testing a lot if certaing methods throws an error and wonder about the right way to do it. I use NUnit and JustMock.

I have two ways of doing this. Using the ExpectedException attribute, and spesifying the type. Or writing as below. The pro with writing as below is that I can also assert the exception.message (if i have made a custom one), and if the test fail i get the exception.message shown too. But I wanted to check with others how you do it. So to sum up:

  1. Is it normal to have a lot of testing for exceptions like this?
  2. Is this the right way to do it:

Just to explain: A supplier offers certain contracts, a Department accepts one contract but can not have more than one contract with the same supplier (but of cource can have different contracts with different suppliers)

    [Test]
    public void Accepting_more_than_one_contract_from_supplier_throws_exception()
    {
        //Arrange
        var department = new Department(Guid.NewGuid(), "1234");
        var supplier = Mock.Create<Supplier>();
        var contract1 = Mock.Create<DeliveryContract>();
        var contract2 = Mock.Create<DeliveryContract>();
        var id = Guid.NewGuid();
        supplier.Arrange(x => x.Id).Returns(id);
        contract1.Arrange(x => x.Supplier).Returns(supplier);
        contract2.Arrange(x => x.Supplier).Returns(supplier);

        //Act
        department.AcceptContract(contract1);

        //Assert
        try
        {
            department.AcceptContract(contract2);
            Assert.Fail("Duplicate contract with supplier did not throw an exception");
        }
        catch (Exception ex)
        {   
            Assert.AreEqual(typeof(ArgumentException),ex.GetType(),ex.Message);
        }
    }

Upvotes: 0

Views: 1666

Answers (2)

michalczukm
michalczukm

Reputation: 10459

As @Ufuk said. Use Assert.Throws<T> where T is an exception type.

If you also want to check the exception message use one of this:

T Assert.Throws<T>( TestDelegate code, string message );
T Assert.Throws<T>( TestDelegate code, string message, 
        params object[] parms);

By the way the data you created for tests shouldn't be mocks but stubs. By stubs I mean objects which only contains data for tests, in your case you provide behaviour for objects - not data.

I'd recommend you use of NBuilder code.google.com/p/nbuilder/. You can create clear stubs by it. It also have great flexibility of creating objects, they can be create randomly or exactly prepared for your test case.

The usage in your case:

var supplierStub = Builder<Supplier>
                .With(supplier => supplier.Id = id)
                .Build();

var contractsStub = Builder<DeliveryContract>.CreateListOfSize(2)
                .All()
                    .With(contract => contract.Supplier = supplierStub)
                .Build();

Upvotes: 2

Ufuk Hacıoğulları
Ufuk Hacıoğulları

Reputation: 38468

You can use Assert.Throws method:

Assert.Throws<ArgumentException>(() => department.AcceptContract(contract2));

or

Assert.Throws<ArgumentException>(() => department.AcceptContract(contract2), "some message");

Upvotes: 6

Related Questions