3

I have been reading about unit tests & Clean architecture and tried to implement something that would involve those two things.

It is my understanding that a Clean architecture is structured so that the methods of the Interactor object can be unit-tested.

But when the use case is something like "Create a file which content is computed from some data in some format", I get confused because it's not unitary (there's the computation of the file content, and the creation of the file, which are both in the use case)

Here's some pseudo-code illustrating my situation :

/* We are in an Interactor (i.e. UseCaseObject)
 * This method 1)computes fileContent and 2)writes it into a file. 
 */
public void CreateFileFromData(someDataInSomeFormat) {
    var parsedData = SomeParser.Parse(someDataInSomeFormat);

    string fileContent = ???; 

    WriteFile(fileContent); 
}

My questions are the following :

  1. Must a method defined in the Interactor be unitary ? (as in, do only one thing)
  2. Must a method defined in the Interactor be unit-tested ? (I see a function, unitary or not, as a testable unit, please correct me if this is incorrect)
  3. Which class must hold the computation of fileContent in a Clean architecture ?

2 Answers2

2

You not telling from where data for computation will be "loaded", but for example lets assume that data will be read from another file.

Your interactor will have three dependecies
- read file
- calculate data for new file
- write file

public class Interactor
{
    public Interactor(IReader reader, ICalculator calculator, IWriter writer)
    { }

    public void DoJob()
    {
        var data = reader.Read();
        var calculatedData = calculator.Calculate(data);
        writer.Write(calculatedData);
    }
}

With this approach Interactor will have responsibility to "combine" steps required to accomplished a task.

You can simply test Interactor by mocking all dependencies.

Where:
IReader and IWriter are Gateways
ICalculator is implementation detail of UseCase which used by Interactor

Must a method defined in the Interactor be unitary ? (as in, do only one thing)

Method should do one thing - execute use case related task. If task requires using of gateways(external resources) or task is to complicated to keep it in one method - you will introduce all required units as dependencies and interactor responsibility will be to "glue" them together.

Must a method defined in the Interactor be unit-tested ? (I see a function, unitary or not, as a testable unit, please correct me if this is incorrect)

Abstract only gateways(external resources) - Then you can test whole logic of interactor. If you writing test first - you will write tests and whole logic can be in the one function(it could/should be ugly spagetti code, which makes tests pass). Then when you see whole picture of implementation you can start moving staff around by moving things to dedicated classes.

Which class must hold the computation of fileContent in a Clean architecture ?

It can be interactor, if it is simple one line computation. But I prefer to introduce dedicated class for computation and introduce it as dependency. While tests will remain in interactor and dedicated computation class will be tested through interactor tests

Fabio
  • 31,528
  • 4
  • 33
  • 72
  • Thank you for your answer. However, it bears no reference to the Clean architecture, which my questions are specifically inherent to. The fact that you mention "where the data will be 'loaded'" seems to indicate that your answer is out of the scope of the Clean architecture, since the latter handles this kind of things through identified notions such as "Entities" and "Gateways". Can you please show more clearly how your answer fits into a Clean architecture ? – Minh-Tâm TRAN Mar 19 '18 at 09:37
  • Thank you ! Much more on spot regarding what I was having difficulties with :) One last thing: even though you answered to my third question, can you also clarify the answer to the whole 3 questions in your answer, for future readers ? It should be, from my understanding : [1:No][2:Yes][3:an implementation detail of the Interactor implementing an interface] Thanks again ! – Minh-Tâm TRAN Mar 19 '18 at 10:54
  • On second thought, after reading [this](https://stackoverflow.com/a/23124879/5317927), it seems that a good unit test is a test that doesn't break if I change the inside of a function. From my understanding, your approach would create a test that breaks the moment I decide to change the inside of the Interactor's function ! (see the very similar example provided in my link) – Minh-Tâm TRAN Mar 19 '18 at 11:08
  • @Minh-TâmTRAN - check updated answer. You need to abstract(mock) only dependencies which makes tests slow(external resources). – Fabio Mar 19 '18 at 11:12
  • I understand your point and think you are 100% right. However, additionally and on another topic, from my understanding, a mock is different from a stub because it can verify that a method has been used or not. So the test using that mock would be coupled to the name of the method used (which is the case in the code within the link I shared, search "// verify things were called"), and in other words, if you change the inside of the tested method, the verification from the mock will fail, resulting in a test breaking for the sole reason that the inside of the function changed - which is bad – Minh-Tâm TRAN Mar 19 '18 at 11:25
  • @Minh-TâmTRAN, in unit tests you need to mock only gateways. Gateways usually changes rarely, more important to not use mocks/stubs/fakes for the business logic of your code. Business logic changes often and you refactor it more often. – Fabio Mar 19 '18 at 18:45
  • 1
    What I've read in this thread helped me and taught me a whole bunch of things (that led to 2 days spent in readings of all sorts) and with some hindsight, I found the answer for my questions. While typing my own answer, I realized that it was all contained in yours. I'm sorry it took so long for my ideas to be mature enough to get to read your answer the way I read it now. I'm marking your answer as the answer :) – Minh-Tâm TRAN Mar 21 '18 at 11:19
0

One core aspect of Clean Architecture is that all application business logic is in Interactor methods. This means u also want to have ur major test focus on Interactors usually using unit tests and low level acceptance tests.

When designing ur Interactor methods u should still follow SRP: there should be only one reason to change. U can also combine Interactors to follow SRP.

If the computation of the file content is application business logic for u it should be in an Interactor method.

For a more detailed discussion about Interactors pls have a look at my post: https://plainionist.github.io/Implementing-Clean-Architecture-UseCases/

plainionist
  • 2,950
  • 1
  • 15
  • 27
  • For constructive purpose, and it is absolutely **not** a feedback about _your_ answer but rather about _my_ question, now that I found the answer to my issue(big thanks to all yours links here and in your article), I realize that in fact I saw WriteFile() only as the main and finishing step of my use case, and it made me reconsider where to put the computation ; while in reality it should have been the opposite, WriteFile code having to be moved away from the Interactor. Obviously, you could not guess all that, I'm just saying it in case it can help you spot the real question in other posts :) – Minh-Tâm TRAN Mar 21 '18 at 11:34