开发者

Unit test a method which is on high abstraction level

开发者 https://www.devze.com 2023-04-10 05:48 出处:网络
A similar topic has been discussed in The value of high level unit tests and mock objects However, I\'d like to describe a specific situation and ask your opinion about how I should write a unit test

A similar topic has been discussed in The value of high level unit tests and mock objects

However, I'd like to describe a specific situation and ask your opinion about how I should write a unit test.

I am developing an ordinary 3-tier application, which uses Entity Framework. Above EF, I have two layers:

  • Repositories: They directly access the EF ObjectContext and do all the CRUD work (actually, these classes are generated with a T4 template). All Repository class has an appropriate interface.
  • Managers: They implement the higher level business logic, they do not access directly the ObjectContext, rather use an appropriate Repository. Managers do not know the concrete Repository-implementation, only the interface (I use dependency injection, and mocks in the unit test).

Without further description, here is the class I'd like to write unit tests for:

public class PersonManager
{
    private IPersonRepository personRepository; // This is injected.

    // Constructor for injection is here.

    public void ComplexMethod()
    {
        // High level business logic
        bool result = this.SimpleMethod1();
        if(result)
            this.SimpleMethod2(1);
        else
            this.SimpleMethod2(2);
    }

    public bool SimpleMethod1()
    {
        // Doing some low-level work with the repository.
    }

    public void SimpleMethod2(int param开发者_如何学Python)
    {
        // Doing some low-level work with the repository.
    }
}

It is really easy to unit test SimpleMethod1 and SimpleMethod2 by instantiating the PersonManager with a mock of the PersonRepository.

But I can not find any convenient way to unit test ComplexMethod.

Do you have any recommendation about how should I do that? Or that should not be unit tested at all? Maybe I should not use the this reference for the method calls in ComplexMethod, rather access the PersonManager itself via an interface, and replace that with a mock too?

Thanks in advance for any advice.


Guillaume's answer is good (+1), but I wanted to give an additional observation. What I see in the code you've posted is the basis for a very common question from people trying to figure out (or argue against) TDD, which is:

"How/why should I test ComplexMethod() since it depends on SimpleMethod1() and SimpleMethod2(), which are already tested and have their own behavior that I'd have to account for in tests of ComplexMethod()? I'd have to basically duplicate all the tests of SimpleMethod1() and SimpleMethod2() in order to fully test ComplexMethod(), and that's just stupid."

Shortly after, they usually find out about partial mocks. Using partial mocks, you could mock SimpleMethod1() and SimpleMethod2() and then test ComplexMethod() using normal mock mechanisms. "Sounds great," they think, "This will solve my problem perfectly!". A good mock framework should strongly discourage using partial mocks in this way, though, because the reality is:

Your tests are telling you about a design problem.

Specifically, they're telling you that you've mixed concerns and/or abstraction levels in one class. They're telling you that SimpleMethod1() and SimpleMethod2() should be extracted to another class which this class depends on. No matter how many times I see this scenario, and no matter how vehemently the developer argues, the tests are proven right in the end 100% of the time.


I don't see what the problem is. You can test your complex method while mocking the repository, there is no problem.

Your would need two unit-tests, each one would use the same sequence of expectations and executions that you have in your tests of the SimpleMethod1 (I assume you already have two unit-tests for SimpleMethod1, one for a return of "true", one for "false") and also the same expectations that you have for your test SimpleMethod2 with a fixed parameter 1, or 2 respectively.

Granted, there would be some "duplication" in your testing class, but that's not a problem.

Also note your tests for SimpleMethod2 should not make any assumption for the parameter passed: in "real-life" you can have only 1 or 2 as a parameter (and that's what your unit-test for ComplexMethod would have), but your unit-tests for SImpleMethod2 should test it whatever the parameter is: any int.

And finally, if ComplexMethod is the ONLY way to call SimpleMethod1 and/or SimpleMethod2, you should consider making these private, and have only unit-tests for ComplexMethod.

Does that make sense?

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号