I see only one plus in this pattern: it improves readability. And there are more minuses:

First, it does not always improve readability. Here is a simple test:

@Test public void testAdditionAndSize() { int expectedSize = 3; int actualSize = list.size(); assertEquals(expectedSize, actualSize); } 

Instead, I could write this:

 @Test public void testAdditionAndSize() { assertEquals(3, list.size()); } 

Has readability suffered?

Secondly , what to do with such a test?

 @Test public void testIsEmpty() { assertTrue(!list.isEmpty()); } 

Is it also possible to create 2 boolean variables, and then compare them?

 @Test public void testIsEmpty() { boolean res = false; boolean actual = list.isEmpty(); assertEquals(res, actual); } 

And thirdly , in my opinion, this pattern complicates the writing of some tests. For example:

 @Test public void testCicleNext() { assertEquals(8, list.next().getId()); assertEquals(7, list.next().getId()); assertEquals(9, list.next().getId()); assertFalse(list.hasNext()); assertEquals(8, list.next().getId()); assertTrue(list.hasNext()); } 

Here before each assertion you will have to create 2 variables. As a result, the test will grow to a huge size and in my opinion it will only get harder to read it.

2 answers 2

In any pattern you can always find such a case when it looks ridiculous. I think that you just gave such an example.

The classic rationale for the 3A pattern (yes, it’s spelled three A) is as follows:

  • the code is divided into three logical parts.
  • The testing function clearly tests two things, with no side effects.

Let's analyze the first test:

 assertEquals(3, list.size()); 

Here it is not always clear whether the size function has side effects (yes, in this case it sounds silly, but in general it does not). And let's say this test wanted to comment out temporarily. And the code in the test below may implicitly depend on side effects. And all flew ... (yes, in this particular test one line, but who knows)

Secondly, what to do with such a test?

 @Test public void testIsEmpty() { assertTrue(!list.isEmpty()); } 

And this test just needs to be written correctly. Somewhere so

 @Test public void testListIsNotEmpty() { bool is_empty = list.isEmpty(); assertFalse(is_empty); } 

See the difference? There are no variables of type res whose value is not clear. Also, if the list should not be empty, then this should be tested, not the opposite (see, I even changed the name of the test).

Now look at the last test.

 @Test public void testCicleNext() { assertEquals(8, list.next().getId()); assertEquals(7, list.next().getId()); assertEquals(9, list.next().getId()); assertFalse(list.hasNext()); assertEquals(8, list.next().getId()); assertTrue(list.hasNext()); } 

In this example, next modifies the source object. And if one line is commented out, then all, the test may collapse.

By the way, this test can be rewritten through a cycle and immediately it becomes more beautiful and easier. And no need to create a bunch of variables.

 @Test public void testCicleNext() { int test_data[] = {8,7,9,8}; for (int i = 0; i < test_data.length(); i++) { int expect = test_data[i]; int actual = list.next().getId(); bool has_next = list.hasNext(); assertEquals(expect, actual); assertTrue(has_next ); } } 

Now, if you need to add another 5-6 test values, it is very simple.

My personal opinion. Inside the tested functions (that is, assertEquals and the like) there should be no complicated constructions. That's why they are unit tests to test the minimum unit of code. And if size is still somehow admissible, then methods that modify an object are not.

If I have a situation that I need to upgrade the function / method that I am testing, then this is the first call that the function is complex and it needs to be broken or rewritten. If the test becomes very large (and you complain about it), then the test is written incorrectly - you need to either break it into two smaller ones, or write it correctly (I, for example, like pluses for this in pluses :)).

But in any case, you do not need to suffer patterns of the brain and apply them everywhere. Need to apply them as needed.

I haven't written in Java for a long time, so there may be minor typos in the code.

    Arrange-Act-Assert is a template for formatting Unit tests. Indicating the separation of dough into 3 parts

    1. Arrange - all necessary preparations and input data.
    2. Act - the actual call to the method that you are testing
    3. Assert - checks that the method does what it needs

    In your example with a list there should be something like this:

     @Test public void testAdditionAndSize() { // Arrange - подготавливаем данные для теста SomeClass obj = new SomeClass; // Act - выполняем метод add list.add(obj); // Assert - проверяем, что объект добавился assertEquals(1, list.size()); assertEquals(obj, list.get(1)); // другие тесты проверяющие что добавился правильный объект, что он не изменился и т.д. }