Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing asserts duplication

I'm studying TDD and experimenting it in my current project. I've noticed that I have to duplicate a lot of asserts in my tests. Here is situation: I have Order class with two constructors first one is default and second one has three params

Order(int customerId, int typeId, decimal amount)

In the OrderTests class I'm checking that assignments are working well

Assert.IsTrue(o.CustomerId == 5 && o.TypeId == 3 && amount == 500)

I have order service class with following create order method as order creation is complex process.

Order CreateOrder(int cusotmerId, int typeId, int amount, moreParams...)

OrderServiceTests class has test for this method and I need to use same assert to check that Order has been created correctly in the CreateOrder service.

Assert.IsTrue(o.CustomerId == 5 && o.TypeId == 3 && amount == 500)
  1. Is it ok to have such duplications in Tests?
  2. Is it make sense to extract methods with same assertions in tests as sometimes number or duplicated asserts maybe more then one? Or such method extractions make tests unreadable?
like image 591
Danil Avatar asked Feb 26 '23 04:02

Danil


2 Answers

If you have multiple ways to create an object, you might want to test the object's state for each of the creation methods (i.e. parametrized constructor as well as factory method). Thus it makes sense to duplicate the assertions.

During refactoring after making your test pass (always remember the mantra: red-green-refactor) if you find duplication not only in your production code but also in your tests, then you should remove it by e.g. using the Extract Method refactoring.

[TestMethod]
public void if_parametrized_ctor_is_called_then_state_should_be_accordingly {
  var order = new Order(customerId, ...);
  ObjectPropertiesShouldBeSetTo(order, customerId, ...);
}

[TestMethod]
public void if_factory_method_is_called_then_state_should_be_accordingly {
  var order = myFactory.CreateOrder(customerId, ...);
  ObjectPropertiesShouldBeSetTo(order, customerId, ...);
}

// Extracted to remove code duplication
public void ObjectPropertiesShouldBeSetTo(Order order, int customerId, ...) {
  Assert.AreEqual(customerId, order.CustomerId);
  Assert.AreEqual(...);
}

It complicates things if you check multiple conditions within one Assert statement as in your example. It reduces test readability and it might be difficult to find the cause if any one of the conditions fails.

like image 146
Dennis Traub Avatar answered Mar 02 '23 16:03

Dennis Traub


If you need to perform the same object validation for multiple tests, splitting out those Asserts to a common method is a good way to reduce the duplication. In your example above, you could have a method called AssertObjectIsValid, and move the common code there.

One other thing regarding your example Assert. Combining multiple checks in a single Assert makes it more difficult to determine from a failure which property was at fault. If you split these into separate Asserts, and provide messages for each, it will make tracking down the error much easier (especially if you use a continuous integration server such as CruiseControl.Net.) Modifying your example:

Assert.IsTrue(o.CustomerID == 5, "CustomerID doesn't match expected");
Assert.IsTrue(o.TypeId == 3, "TypeID doesn't match expected");
Assert.IsTrue(amount == 500, "Amount doesn't match expected");
like image 37
Pedro Avatar answered Mar 02 '23 17:03

Pedro