9

I have three functions:

  • ValidateUsername(), which determines if a string is a valid username according to some rules
  • SetUsername() which sets a string as a user's Username if it passes validation
  • AddNewUser(), which creates a new user and, among others, sets a string as his or hers Username using SetUsername()

All three should fail if the supplied username string is incorrect, yet ValidateUsername() is where the rules reside. How would I unit test this? Testing validation only in ValidateUsername() leaves the possibility that someone will mistakenly omit validation and set the username directly in AddNewUser(). On the other hand, repeating all different validation test cases for all functions seems counterproductive. What's the best practice?

io-quois
  • 101
  • 1
  • 1
  • 3
  • see also: [How to manage success dependency between unit tests](http://programmers.stackexchange.com/questions/224829/how-to-manage-success-dependency-between-unit-tests) – gnat Jun 22 '15 at 17:26
  • Are these methods all on the same class? – Ben Aaronson Jun 22 '15 at 21:13

3 Answers3

12

Unit testing is called that way because you are intending to test units of work, in your case each method is a unit of work and not the whole integration between them.

Let's assume your business requirement for ValidateUsername() method is to validate the user name according to certain requirements. Your unit test in this case should verify that validation was correct and rules are implemented correctly.

Now seems like for you other methods, one of the business requirements is to call validation method - do just that. Ensure that the ValidateUsername was called indeed. You don't care whether it was executed correctly or not - that part is to be tested by your previous test for ValidateUsername unit.

Alexus
  • 2,368
  • 15
  • 24
  • 1
    Thanks, I ended up mocking the validating class containing the validation method to check if it was called. This seemed practical as I want all validation logic to live inside it. – io-quois Jun 24 '15 at 12:17
  • 1
    Well done, Mocking is what needed to do :) – Alexus Jun 24 '15 at 17:24
4

You can verify that the validation function was called by testing the other two functions with one invalid name and one valid name. Or you can separate the logic into UsernameValidator and UserService, and inject the validator into the user service. Then you test the user service by mocking the validator.

kevin cline
  • 33,608
  • 3
  • 71
  • 142
1

If I'm interpreting your question correctly, AddNewUser() calls SetUsername(), and SetUsername() calls ValidateUsername(). Two options come to mind:

1) Keep the existing structure, and write unit tests for the "one thing" that each function is responsible for, while assuming the "happy path" for its dependencies. So the ValidateUsername() tests would check valid and invalid usernames, the SetUsername() test would pass in a valid username and make sure that username ends up in the right place, and the AddNewUser() test would pass in valid user-construction data and make sure a user got added using that data.

2) Change the structure to something "flatter" so that the functions no longer depend on each other. So you might have a AttemptToAddUser() function that calls ValidateUsername(), then SetUsername(), then AddNewUser() all at the same level of the call stack.

The optimal answer depends heavily on your exact code, but I would expect it to be either #1 or something between #1 and #2. It might make perfect sense to pull ValidateUsername() up to the same level as AddNewUser(), but I have a hard time imagining a SetUsername() function being of any use outside the AddNewUser() function (assuming it's not a method on a User object or something).

And of course, regardless of how the unit tests work, you probably still want a small number of integration tests that check all of these functions together.

Ixrec
  • 27,621
  • 15
  • 80
  • 87
  • Option (1) would not test all relevant code-paths of `SetUsername()` and `AddNewUser()`. Option (2) calls for bad coding habits (copy-and-pasting idioms). I don't like either. – Tilman Vogel Feb 08 '21 at 23:16