tags:

views:

171

answers:

3

Hi,

Suppose the following model

public class Product {

    private Integer requiredQuantity;
    private Integer allowedQuantity;

    // getters and setters

} 

public class Order {

    public void allowsOrder(List<Product> productList) throws AppException {

        Integer allowedQuantity = 0;
        Integer requiredQuantity = 0;
        for(Product product: productList) {
            if(product.getAllowedQuantity().compareTo().product.getRequiredQuantity() > 0)
                throw new AllowedQuantityIsGreaterThanRequiredQuantity();

                allowedQuantity += product.getAllowedQuantity();
                requiredQuantity += product.getRequiredQuantity();                          
        }

        switch(allowedQuantity.compareTo(requiredQuantity)) {
            case 0:
                setOrderStatus(OrderStatus.ALLOWED);
            break;
            case -1:
                if(allowedQuantity.equals(0))
                    setOrderStatus(OrderStatus.DENIED);
                else
                    setOrderStatus(OrderStatus.PARTIALLY_ALLOWED);
            break;
        }
    }
}

So i have developed (BEFORE code above) a Test Driven Development according to:

obs: for each test method a dataProvider (by using TestNG) supplies parameters

public void successAllowedOrder(List<Product> productList, Order beforeAllowingOrderMock, Order afterAllowingOrderMock) {
    Integer allowedQuantity = 0;
    Integer requiredQuantity = 0;
    for(Product product: productList) {
        allowedQuantity += product.getAllowedQuantity();
        requiredQuantity += product.getRequiredQuantity();
    }

    assertEquals(allowedQuantity, requiredQuantity);

    assertNull(beforeAllowingOrderMock.getOrderStatus());
    assertEquals(OrderStatus.ALLOWED, afterAllowingOrderMock.getOrderStatus());

    // beforeAllowingOrderMock is now a implementation
    beforeAllowingOrderMock = new Order();

    beforeAllowingOrderMock.allowsOrder(productList);

    assertNotNull(beforeAllowingOrderMock.getOrderStatus());
    assertEquals(beforeAllowingOrderMock.getOrderStatus(), afterAllowingOrderMock.getOrderStatus());
}

public void successPartiallyAllowedOrder(List<Product> productList, Order beforeAllowingOrderMock, Order afterAllowingOrderMock) {
    Integer allowedQuantity = 0;
    Integer requiredQuantity = 0;
    for(Product product: productList) {
        allowedQuantity += product.getAllowedQuantity();
        requiredQuantity += product.getRequiredQuantity();
    }

    assertTrue(requiredQuantity > allowedQuantity);

    assertNull(beforeAllowingOrderMock.getOrderStatus());
    assertEquals(OrderStatus.PARTIALLY_ALLOWED, afterAllowingOrderMock.getOrderStatus());

    // beforeAllowingOrderMock is now a implementation
    beforeAllowingOrderMock = new Order();

    beforeAllowingOrderMock.allowsOrder(productList);

    assertNotNull(beforeAllowingOrderMock.getOrderStatus());
    assertEquals(beforeAllowingOrderMock.getOrderStatus(), afterAllowingOrderMock.getOrderStatus());
}

public void successDeniedOrder(List<Product> productList, Order beforeAllowingOrderMock, Order afterAllowingOrderMock) {
    Integer allowedQuantity = 0;
    Integer requiredQuantity = 0;
    for(Product product: productList) {
        allowedQuantity += product.getAllowedQuantity();
        requiredQuantity += product.getRequiredQuantity();
    }

    assertTrue(allowedQuantity == 0);

    assertNull(beforeAllowingOrderMock.getOrderStatus());
    assertEquals(OrderStatus.DENIED, afterAllowingOrderMock.getOrderStatus());

    // beforeAllowingOrderMock is now a implementation
    beforeAllowingOrderMock = new Order();

    beforeAllowingOrderMock.allowsOrder(productList);

    assertNotNull(beforeAllowingOrderMock.getOrderStatus());
    assertEquals(beforeAllowingOrderMock.getOrderStatus(), afterAllowingOrderMock.getOrderStatus());
}

public void failureAllowedQuantityIsGreaterThanRequiredQuantity(List<Product> productList, Order beforeAllowingOrderMock) {
    Integer allowedQuantity = 0;
    Integer requiredQuantity = 0;
    for(Product product: productList) {
        allowedQuantity += product.getAllowedQuantity();
        requiredQuantity += product.getRequiredQuantity();
    }

    assertTrue(allowedQuantity > requiredQuantity);

    try {
        beforeAllowingOrderMock.allowsOrder(productList);
    } catch(Exception e) {
        assertTrue(e instanceof AllowedQuantityIsGreaterThanRequiredQuantity);
    }

    // beforeAllowingOrderMock is now a implementation
    beforeAllowingOrderMock = new Order();

    try {
        beforeAllowingOrderMock.allowsOrder(productList);
    } catch(Exception e) {
        assertTrue(e instanceof AllowedQuantityIsGreaterThanRequiredQuantity);
    }
}

If i have developed failure case first and for each test method a mock and, after mock, a implementation, is it a Test Driven Development approach ?

regards,

+4  A: 

While there may be other conditions that people place on it, generally if you're writing tests before and as you code, it is probably test driven. You usually come up with tests that fail at first, and then you write code to make them pass.

Matt
+4  A: 

As long as there are tests, you can't look at the code and see if it was developed using TDD or not. The TDD cycle looks like this:

  1. write test.
  2. make it pass.
  3. refactor.

One subtle thing is that you may not add any new functionality unless it is to make a failing test pass.

The number of mocks used is irrelevant.

Buhb
+3  A: 

Who is invoking the method such as successAllowedOrder(), successPartiallyAllowedOrder(), successDeniedOrder() ? I'm asking cause TestCases usually do not have parameters.

Typically unit-tests written applying TDD follow a triple A pattern:

  • Arrange
  • Act
  • Assert

There also are fixtures, the environment where the test will be executed, created and cleaned up by the setup() and tearDown() methods. A fixture is common to all tests in the same class.

With your Product class, this would gives something along the lines of :

public class TestOrder extends TestCase
{
    // assume the fixtures creates the Product instances used for the tests here

    void TestValidOrder() 
    {
      // arrange - create an order
      // act - do something on the order - could be empty if testing the creation
      // assert - test the status of the order
    }

}

A point of design: why doesn't the loop to compute the allowedQuantity and requiredQuantity belong to the Order class ?

Have a look at the bowling game episode, it's really a great tutorial to TDD. Last, you one can do TDD without resorting to mock objects.

philippe
I use TestNG in order to pass parameter. Very good tutorial. Thank you.
Arthur Ronald F D Garcia
Hi Philippe, as said: why doesn't the loop to compute the allowedQuantity and requiredQuantity belong to the Order class ? How can i test it whether allowsOrder implementation is not done ? I need a way to test in mock, do not ?
Arthur Ronald F D Garcia
Arthur, this depends on the test, on the fixture the test is running in. When you create the fixture - the test environment you know what the result will be -It can be hard-coded. if you use the same algorithm you'll always get the same result.
philippe