9

Background: I am new to testing in general, and have been studying it in context of JavaScript, specifically React.js, front-end development (actually new to this as well). For the question, I have these 2 similar cases:

Case 1: My code and a library

I'm using UI library and aside from its decent design, I want to leverage its form validation utilities. I want to test this validation, but not sure how to do it.

  • Option 1: Just test for the behaviors I expect

    It would be testing if my field (which is a sub-module of the library itself) receives the library-specific classes for fields with errors as expected, and if these errors prevent from calling the submit handler I passed to the form (also sub-module of the library).

    • Problem A with Option 1

      I am very likely to be duplicating tests that are already covered by the library itself. (e.g. I am testing if the field receives the error classes when the input is invalid when the library itself has tested if it does so, given the right configuration.)

    • Problem B with Option 1

      It slightly couples the test code to the library, i.e. I have to use library-specific classes and markup to evaluate my code.

      Okay, this is actually a side-question, is this a bad thing for a test? Is this making the test brittle, or is what they call 'contract' that is actually necessary for unit tests?

  • Option 2: Test which sub-module is used and what configuration is passed

    • Problem A with Option 2

      This is much more brittle, I think, It's almost like repeating the implementation (i.e. repeating the type sub-module used, and the configuration passed to it.)

    • Problem B with Option 2

      Same as Option 1's Problem A -- coupling test to the library's API, specifically its configuration API. (Again, not sure if this is just test being brittle, or contract being written)


Case 2: My code and its sub-component

I have my good 'ol to do app. Its to do list component has addTodo() method and it passes to its sub-component to do field. I want to test its feature to add todo item, but not sure on how to do this either.

  • Option 1: Again, just test for the behaviors I expect

    Test `to do app` that if input and submit with sub-component `to do field`, another `to do item` is added. (This is implemented by passing `addTodo()` method from `to do app` to `to do field` as onSubmit handler)
    
    • Problem A with Option 1

      If I'll do this, is there still a point in unit testing the to do field alone? If I already unit-tested to do field that it calls any onSubmit handler upon submitting, wouldn't this to do app test case indirectly repeating to do field's test case??

  • Option 2: Test if correct sub-component is used and what configuration is passed (Similar to Case 1's option 2)

    Its specs would look like this:

    • Unit test to do app to assert...
      • that to do app's addTodo() method adds a todo item properly
      • that to do app renders to do field sub-component
      • and that to do app passes addTodo() to as onSubmit handler to to do field
    • then also unit test to do field to assert that...
      • that to do field calls the onSubmit, which is received from parent component, during submit

The Question

Which of these options are better, especially if I favor BDD over TDD? Also, please correct me if you noticed I misunderstood anything. Thank you.

8
  • 2
    I'd avoid testing 3rd party library code in general. Your approach concerns me though because you're coupling your validation (which is business logic) to a specific UI. Change UIs (or even to another library) and it seems like you'll be throwing your business logic away.
    – Andy
    Jul 11 '17 at 21:09
  • What would be the better approach? Jul 11 '17 at 21:29
  • Keep your business logic in a business library that doesn't have any dependencies on UI. An N-layer, DDD, onion, whatever you want to call it approach.
    – Andy
    Jul 11 '17 at 21:31
  • Hmm, my understanding is that in BDD, in order to test the behavior, you inevitably involve the UI, this is what I've observed in JS front-end development. Jul 11 '17 at 22:16
  • Not sure where you got that idea, that sounds off to me. MSpec for example is a BDD framework. Its mostly a shift in your thinking about how to build the unit test. Integration / acceptance tests may involve the UI, but nothing in BDD to me suggests it has to involve the UI. en.wikipedia.org/wiki/Behavior-driven_development
    – Andy
    Jul 11 '17 at 22:42
0

You are testing a user interface. This is exactly what functional testing was created to do. You want to test the behavior of the interface you've created, specifically:

  • When the screen is first loaded, is the submit button visible/selectable? If it shouldn't be, you can test for that.
  • Enter invalid data into your text field. Use all the different types of invalid data that you have configured to check. The submit button should probably be visible/selectable, but clicking it should generate some kind of error message. Check that the error message displays as you expect it to.
  • Enter valid data into your text field. The submit button should be visible/selectable, and clicking on it should produce the correct result. Check that the correct result is displayed.

Using functional testing like this prevents coupling to your library. Changing the library should result in these tests passing. Even dropping the library and rolling your own validation routines should result in these tests passing.

If you haven't chosen a functional testing tool, you may want to consider Selenium. It will enable you to write and run functional tests.

0

I think you have outlined approaches in your Case 2 above. What you are getting at is the distinction between unit and integration tests. (I actually find that the language of TDD vs. BDD is not that helpful here.) In a "unit test" test, the behavior of a specific piece of code is validated; and an integration tests, the interaction between different pieces of code is validated.

Let's say that the React validation library changes in such a way that it breaks your app. If you upgrade your library, you'd like a test to fail in this case. And ideally, you wouldn't want all your tests to fail.

Like you say, you can write specific tests that validate individual the behaviors of your code, such as:

a. if I enter bad data a css class gets applied to an input, and 
b. if there's a css class on an input the submit button is disabled. 
   If your library does this, it's tested there, right?
c. if the submit button is disabled the form cannot be submitted, etc. 

These very specific behavior tests are nice when they fail, as it's clear what's going on. The downside is that sometimes the chain, or coupling between them, can have flaws: a step can be left out, or the contract with an external tool can change slightly and then go undetected with the unit test. Or it can just be hard for the reader to piece them all together.

And a callout to (b) above. If a your code is dependent on a specific library behavior, you need to figure out how you will maintain trust in it. If it's the main behavior of the library, maybe you can just trust it; but if it's a less central piece of behavior, you may want to write your own (redundant) test. I seldom find that testing that a configuration setting is correct raises my confidence much, but people do in fact do this.

Consider a higher level example (perhaps via Selenium) that tests that the system behaves within the context of the app: entering a specific value in an input box shows an error and will not submit to the server. It's nice as it's not tied to implementation details: if a css class is changed globally, the test stays green if the app still works. The downside is that the test is covering a wide swatch of behavior, so it will be more brittle than a unit test. If any of the units tests above fail, this integration test will also probably fail. This can turn people off to these tests, but in fact, this is the only test that will catch some subtle change in your external library that you missed when upgrading.

I recommend a testing strategy include a thoughtful combination of different types of tests, balancing the benefits of these two specific approaches.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.