TDD: Stub, Mock, or None of Above

I am trying to learn TDD by applying it to my simple project. Some details (and an earlier question) are here:

TDD: Testable Class Writing Assistance

Specificity: I have a PurchaseOrderCollection class that has a private PurchaseOrders list (passed in the constructor), and BuyOrders has the IsValid boolean property. PurchaseOrderCollection has a HasErrors property that returns true if any of the BuyOrders in the list has IsValid as false. This is the logic I want to test.

[TestMethod] public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False() { List<PurchaseOrder> orders = new List<PurchaseOrder>(); orders.Add(new PurchaseOrder(--some values to generate IsValid false--)); orders.Add(new PurchaseOrder(--some values to generate IsValid true--)); PurchaseOrderCollection collection = new PurchaseOrderCollection(orders); Assert.IsTrue(collection.HasErrors); } 

This is similar to my previous question that this test is too related to the fact that I need to know the logic of what makes PurchaseOrder IsValid false or true in order to pass the test when really this test should not care. The question is different (imo) in that the classes themselves are not a problem.

Essentially, I want to be able to declare a PurchaseOrder with IsValid false or true, without knowing anything more about what PurchaseOrder is.

From my limited knowledge of TDD, this is what you use Stubs or Mocks for. My main question is, is that right? Or should I use a different method for this? Or am I completely messed up and am I just writing this test and thinking about it wrong?

My initial thought was to just use some kind of fake structure and create a PurchaseOrder that always returns true or false. From what I read, I needed to declare IsValid virtual. So my second thought was to modify it to add iPurchaseOrder as an interface for PurchaseOrder and just create a fake PurchaseOrder that always returns false or true. Are there both of these valid ideas?

Thanks!

+4
source share
7 answers

You are on the right track by creating a stub or layout. I prefer to use the Mocking environment.

How it works, using a mocking structure, you would like to make fun of your PurchaseOrder class to abstract its implementation. Then configure the wait called IsValid, and when it is called, return this value.

Moq example if you use C # 3.0 and .NET Framework 3.5:

 [TestMethod] public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False() { var mockFirstPurchaseOrder = new Mock<IPurchaseOrder>(); var mockSecondPurchaseOrder = new Mock<IPurchaseOrder>(); mockFirstPurchaseOrder.Expect(p => p.IsValid).Returns(false).AtMostOnce(); mockSecondPurchaseOrder.Expect(p => p.IsValid).Returns(true).AtMostOnce(); List<IPurchaseOrder> purchaseOrders = new List<IPurchaseOrder>(); purchaseOrders.Add(mockFirstPurchaseOrder.Object); purchaseOrders.Add(mockSecondPurchaseOrder.Object); PurchaseOrderCollection collection = new PurchaseOrderCollection(orders); Assert.IsTrue(collection.HasErrors); } 

Edit:
Here I used the interface to create a PurchaseOrder layout, but you don't have one either. You can mark IsValid as virtual and make fun of the PurchaseOrder class. My rule of thumb when to go is to use a virtual machine first. Just to create an interface, so I can mock the object without any architectural reason, this is the smell of code for me.

+6
source

... this test is too much related to the fact that I should know the logic of what PurchaseOrder IsValid does false or true to pass the test when this test really does not care ...

I would actually argue the opposite - that for your test to know that validity is modeled as a logical value in a purchase order means that your test knows too much about the implementation of PurchaseOrder (given that this is actually a PurchaseOrderCollection test). I have no problem using real knowledge (i.e. actual values ​​that would be valid or invalid) to create the appropriate test objects. Ultimately, this is really what you are testing (if I give my collection a purchase order with funny values, will it tell me correctly if there are any errors).

In general, I try to avoid writing an interface for an entity, such as PurchaseOrder, if there is no reason for this other than for testing (for example, there are several types of PurchaseOrders in production, and an interface is the best way to model this).

It’s good when testing shows that your production code can be better developed. However, it is not very good to modify the production code to make the test possible.

As if I didn’t write enough, here is another sentence - and that’s how I would really solve it in real life.

Create a PurchaseOrderValidityChecker application with an interface. Use this when setting up isValid boolean. Now create a test version of the validation that allows you to specify which answer to give. (Note that this solution also probably requires a PurchaseOrderFactory or equivalent to create PurchaseOrders, so that every purchase order can be provided with a link to PurchaseOrderValidityChecker when it is created.)

+2
source

I recently asked a somewhat similar question about testing. Do not forget about this: do the simplest thing you need to do, and then refactor if necessary. I personally try to keep the big picture in my head, but I also resist the urge to overestimate my decisions. You can add two PurchaseOrder fields to your test class, where it is valid, and one is not valid. Use these fields to add the PurchaseOrderCollection to the state you want to check. In the end, you need to learn the layout, but in this case you don’t need a sledgehammer when a regular hammer solves the problem. You get no value using the PurchaseOrder layout instead of the specific PurchaseOrder, which is in the state you want.

Most importantly, you will get much more from testing the behavior of the PurchaseOrderCollection, rather than just checking the status of your PurchaseOrderCollection. After your tests confirm that the PurchaseOrderCollection can be placed in different states, the more important tests are the behavioral tests. Place the collection of purchase orders in both valid and invalid state in any way that you think is appropriate (mocking or creating new classes in the desired state) and verifies that the logic for each PurchaseOrderCollection state is correct, and not just PurchaseOrderCollection is in a valid / invalid state.

PurchaseOrderCollection will always depend on another class, as it is a specialized collection. Knowing that IPurchaseOrder has an IsValid property is no different from knowing that a particular PurchaseOrder has an IsValid property. I stick with the simplest thing that works, for example. a specific PurchaseOrder if you have no reason to believe that you will have several types of PurchaseOrders on your system. At this point, the PurchaseOrder interface will make more sense.

+1
source

Maybe I am missing some context, but it seems to me that you should "connect" your test in the manner of your example, otherwise you will not actually test anything (except for the IsValid property, which is trivial).

Taunting a purchase order doesn't work - you tested the layout, not the real class.

using a stub - the same

it is normal - if not necessary - to check the white box using TDD

+1
source

First, remember that you are testing a collection, not a PurchaseOrder , so where your efforts go. It depends on how complicated the PurchaseOrder . If this is a simple object with explicit behavior, then it probably makes sense to just instantiate. If it is more complex, then it makes sense to extract the interface as you described.

The next question that arises is what is in this interface. What role should objects in a collection play? Perhaps you only need to know if they are valid, in which case you can extract the IValidatable and narrow down the dependencies in the code. I don't know what is true in this case, but I often find that I can use interfaces to push me to a more focused code.

+1
source

I am not an expert in unit testing, but here is what I have done in the past. If you have a PurchaseOder class that may be valid / invalid, then I am sure that you also have unit tests for them to make sure that they really confirm. Why not call these methods to create valid and invalid PurchaseOrder objects, and then add them to your collection?

0
source

Do both of these valid ideas exist?

Yes.

You can also create object material that can return both valid and invalid BuyOrders files.

0
source

All Articles