Warning people with knowledge of good coding practices and faint heart should skip this post. Viewer Discretion is advised.
This post is part of series dedicated to anti-patterns in test automation. You can check the whole series here. Today we will talk about Parameters and what happens if you have too many of them
I’ve put a lot of stuff here some annoying some terrible.
But this one is the best example: „the fact that you can do something doesn’t mean you should.”
So let’s take a look. At Parameters Spam
[csharp]
[TestMethod]
public void Example_Of_Workflow_Test_More_close_To_Reality()
{
Assert.IsTrue(ShipNotficationUXWorkflow.VerifyViewStatusGrid(userID, orderCountry, orderDeliveryAdress, orderDeliveryAdress2, orderDeliveryAdress2, orderDeliveryAdress3, orderDueDate, orderID, orderProductCount, orderPromotion, orderStatus, orderTotal, orderTotalBeforeTax, orderDeliveryAdress, userAdress, userAffiliation, userBirthDate, userBloodType, userDinnerPreferences, userFunFacts, userOrderDate, userOrderID));
}
[/csharp]
well in this form is not readable
[csharp]
[TestMethod]
public void Example_Of_Workflow_Test_More_close_To_Reality_Formated()
{
Assert.IsTrue(ShipNotficationUXWorkflow.VerifyViewStatusGrid(userID,
orderCountry, orderDeliveryAdress, orderDeliveryAdress2,
orderDeliveryAdress2, orderDeliveryAdress3, orderDueDate,
orderID, orderProductCount, orderPromotion, orderStatus,
orderTotal, orderTotalBeforeTax, orderDeliveryAdress, userAdress,
userAffiliation, userBirthDate, userBloodType, userDinnerPreferences,
userFunFacts, userOrderDate, userOrderID));
}
[/csharp]
Ok, so you probably asking your self. What the hell is workflow?
Short answer biggest mistake that any architect could make we will talk about them in future for now. Let 's just say it is in theory bright idea to avoid code duplication in the test. You have to only insert required parameters for the workflow and rest of the magic is happening behind the scenes.
Of course, it never worked, and it never could work, but it is not topic for today.
Uncle Bob and lots of other experts say that ideal method should have zero parameters, but in the real world, it is rarely possible. So 1-2 are acceptable. Three is stretching, and four almost always means refactor.
I doubt in his wildest dreams he would expect to see this.
So why it is bad?
The most obvious reason is you won’t be able to read what is going on. Also when all of those parameters are strings (like in this case) it is easy to screw up and put value in wrong place.
I think I will show you how it actually looked like:
[csharp]
[TestMethod]
[DataSource("SomeCsvWithData")]
public void Example_Of_Workflow_Test_Real_Deal()
{
Assert.IsTrue(ShipNotficationUXWorkflow.VerifyViewStatusGrid(TestContext.DataRow["UserID"].ToString(), TestContext.DataRow["OrderCountry"].ToString(), TestContext.DataRow["OrderDeliveryAdress"].ToString(),
TestContext.DataRow["OrderDeliveryAdress2"].ToString(), TestContext.DataRow["OrderDeliveryAdress2"].ToString(), TestContext.DataRow["OrderDeliveryAdress3"].ToString(),
TestContext.DataRow["OrderDueDate"].ToString(), TestContext.DataRow["OrderID"].ToString(), TestContext.DataRow["OrderProductCount"].ToString(),
TestContext.DataRow["OrderPromotion"].ToString(), TestContext.DataRow["OrderStatus"].ToString(), TestContext.DataRow["OrderTotal"].ToString(),
TestContext.DataRow["OrderTotalBeforeTax"].ToString(), TestContext.DataRow["OrderDeliveryAdress"].ToString(), TestContext.DataRow["UserAdress"].ToString(),
TestContext.DataRow["UserAffiliation"].ToString(), TestContext.DataRow["UserBirthDate"].ToString(), TestContext.DataRow["UserBloodType"].ToString(),
TestContext.DataRow["UserDinnerPreferences"].ToString(), TestContext.DataRow["UserFunFacts"].ToString(), TestContext.DataRow["UserOrderDate"].ToString(),
TestContext.DataRow["UserOrderID"].ToString()));
}
[/csharp]
Another problem is that such long list suggests that method body is trying to do more than it should.
It is also debugging mess. AS Uncle Bob said
„Arguments are hard. They Take a lot of conceptual power” – [expand]Clean Code chapter 3 Functions, section „Function Arguments” [/expand]
Ok, so what can we do.
The most straightforward option is to inline the workflow.
It would look something like that.
[csharp]
public void Example_Of_Workflow_Test_Real_Deal()
{
string userID = TestContext.DataRow["UserID"].ToString();
int OrderProductCount = Int32.Parse(TestContext.DataRow["OrderProductCount"].ToString());
//about 40 lines in this style
/*
*
* some code
* body of the test
*
*/
Assert.IsTrue(assertion)
}
[/csharp]
But that would leave you with the big block of assignments at the start or having them spread throughout the test. Personally, I am in favour of this approach cause it allows us to fix other issues. But we will do it when we deal with workflows altogether.
Unfortunately in this case due to political reasons, it is impossible.
So we have to group our parameters into objects.
And Pass it down.
[csharp]
[TestMethod]
[DataSource("SomeCsvWithData")]
public void Example_Of_Workflow_Test_Real_Deal()
{
var user = new User(TestContext.DataRow["UserID"].ToString(),
//other parameters
);
var order = new Order(TestContext.DataRow["OrderID"].ToString(),
//other parameters
);
Assert.IsTrue(ShipNotficationUXWorkflow.VerifyViewStatusGrid(user, order));
}
[/csharp]
Unfortunately, this one is dealing with the symptom, not the disease but sometimes we have to do one step at a time.
If you noticed. The test is data driven. So we can make it easier for our selves.
One option is creating the constructor that will accept test context and take data straight from it. The only issue is you will need to make sure that naming convention is kept between all data files.
[csharp]
[TestMethod]
[DataSource("SomeCsvWithData")]
public void Example_Of_Workflow_Test_Real_Deal()
{
var user = new User(TestContext);
var order = new Order(TestContext);
Assert.IsTrue(ShipNotficationUXWorkflow.VerifyViewStatusGrid(user, order));
}
[/csharp]
Another option is to create a generic mapper that will map data from that excel to object we want to parse. This option is much easier to use but harder to prepare.
You will need to refresh reflections and generic to be able to write it.
[csharp]
[TestMethod]
[DataSource("SomeCsvWithData")]
public void Example_Of_Workflow_Test_Real_Deal()
{
var user = TestContextHelper.GenereteObjectFromTestData<User>();
var order = TestContextHelper.GenereteObjectFromTestData<Order>();
Assert.IsTrue(ShipNotficationUXWorkflow.VerifyViewStatusGrid(user, order));
}
[/csharp]
Unfortunately writing this generic is beyond the scope of this article. Let me know if you want to see it I will put it in a separate post.
I know that this case seems extreme. But sometimes it is easier to see the problem when it is taken to its logical extreme.
It is a long time since my last post flower. I have lots of materials but this post is also the hardest to do And take the biggest junk of my time.
But my goal is to post one every 2 months. Since I am a month behind schedule Next one will come and of April Begining of may. And Another one in June.
Ps. I apologize for poor code formatting but my plugin for it doesn’t’ work properly. I am looking for new one if someone can recommend it to me I will be glad.