简体   繁体   中英

Is Angular component unit testing actually unit testing?

I'm starting to learn unit testing, and understand the primary objective is to test a system in isolation. Therefore, any dependencies should be stubbed, mocked, etc.

According to the Angular documentation on testing, you should mock whatever is provided via dependency injection. So far, so good. But then, as shown in the code below, you can simply import other required modules, components, directive, pipes through a shared or feature module.

Is this really unit testing then? If the target system under test is a component, isn't this actually testing its entire module?

Or is this just a concession that it'd be near impossible to mock all the requirements in a real world application?

beforeEach(async(() => {
  const routerSpy = createRouterSpy();

  TestBed.configureTestingModule({
    imports:      [ SharedModule ],
    declarations: [ HeroDetailComponent ],
    providers: [
      { provide: ActivatedRoute, useValue: activatedRoute },
      { provide: HeroService,    useClass: TestHeroService },
      { provide: Router,         useValue: routerSpy},
    ]
  })
  .compileComponents();
}));

Is this really unit testing then? If the target system under test is a component, isn't this actually testing its entire module?

The definition of unit testing is to test small units of source code, and components are in fact small units of source code.

But then, as shown in the code below, you can simply import other required modules, components, directive, pipes through a shared or feature module.

The smallest unit of source code that can be tested in JavaScript is a pure function . If you're being a purest then anything larger is no longer the smallest unit, but Angular is an Object Oriented framework so under that paradigm we can say that an object is the smallest unit. So things like components, services, activators, resolvers which are all objects can be covered by a unit test.

Or is this just a concession that it'd be near impossible to mock all the requirements in a real world application?

Good luck trying to test a system after it has been built.

If you want to build something that can be tested, then write the tests first.

https://en.wikipedia.org/wiki/Test-driven_development

Pure Components

A pure component is one without external dependencies, does not mutate an external state and generates an equal output to the same inputs. It's impossible for a component that has a user form to be pure for the same way that new Date() is not pure.

Unit testing frameworks understand these real-world problems and offer solutions as mocking, spying, etc. to allow the tester to cover the impure source code with tests that verify expectations in a pure repeatable way.

That's all you can do, but it's still unit testing and that's what is important.

(Heavily opinion-based so feel free to disagree) That's not really specific to angular, for exemple if you are unit-testing a Python function that uses numpy you'll still import numpy in your UT. So here you could import material components for exemple, and you can't really mock them (and even if you could it's not really a good idea, you want to make sure you are using them correctly). Basically I'd say in unit testing you are mocking/stubbing your other functions (heroService here is mocked) and http requests responses but you can only assume external library work as intended so you still import and use them.

You write

the primary objective is to test a system in isolation

Yes - and no. Unit-testing is for findings bugs that can be found in the isolated software. This does not mean that you actually must isolate the code during testing. In other words, you do not necessarily have to mock all dependencies. For example, if your code uses the sin() function, this is also a dependency to a different code part, but such a dependency does typically not harm your ability to properly test the code.

The general rule is, that mocking should be done for a reason. Good reasons are:

  • You can not easily make the depended-on-component (DOC) behave as intended for your tests.
  • Does calling the DOC cause any non-derministic behaviour (date/time, randomness, network connections)?
  • The test setup is overly complex and/or maintenance intensive (like, need for external files)
  • The original DOC brings portability problems for your test code.
  • Does using the original DOC cause unnacceptably long build / execution times?
  • Has the DOC stability (maturity) issues that make the tests unreliable, or, worse, is the DOC not even available yet?

As you can easily see, the use of a sin() function would typically not fall under the reasons listed above. You would have to judge if that applies also to your scenario.

However, one conclusion of the above point is, that you can also do unit-testing if you do not mock all of the dependencies. But - what is then the distinguising property of unit-testing? What distinguishes unit-tests from, for example, integration-tests, is the goal that you pursue with the respective test.

If your goal was to find bugs in the way another component is addressed (like, if the other component's functions are called in a wrong order or with wrong arguments, or if the return values are not in the expected form etc.), then this would not be unit-testing, but integration testing: You would not even be able to find these bugs when testing your component in isolation - you need the other component to find them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM