Advertisement
Guest User

Untitled

a guest
May 27th, 2016
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.58 KB | None | 0 0
  1. # Anti-tests
  2. What follows is a real world example that I've come across more than once in a professional context.
  3.  
  4. ## The requirement
  5. Let's say we've been tasked with returning `400` when `GET` `/users/<userId>` is called with a negative `userId`.
  6.  
  7. ## Integration test
  8. The requirement can be turned into an integration test that actually hits the endpoint and checks that a `400` is returned:
  9.  
  10. ```java
  11. @Test
  12. public void getUser_InvalidUserId_400() {
  13. expect().statusCode(400).when().get("/users/-1");
  14. }
  15. ```
  16.  
  17. ## Implementation
  18. A ubiquitous style of implementation may look something like this: (you may or may not like it, but it's ubiquitous)
  19.  
  20. ```java
  21. public class UserResource {
  22.  
  23. @Inject
  24. private UserService userService;
  25.  
  26. @GET
  27. @Path("/users/{userId}")
  28. public Response getUser(@PathParam("userId") final Long userId) {
  29. try {
  30. return Response.ok(userService.findById(userId)).build();
  31. } catch (final UserException e) {
  32. return Response.status(e.getErrorCode()).build();
  33. }
  34. }
  35. }
  36. ```
  37.  
  38. ```java
  39. public UserService {
  40.  
  41. @Inject
  42. private UserDao userDao;
  43.  
  44. public User findById(final Long userId) throws UserException {
  45. if ((userId == null) || (userId <= 0)) {
  46. throw new UserException("Invalid arguments", 400);
  47. }
  48. return userDao.findById(userId);
  49. }
  50. }
  51.  
  52. ```
  53. ## Unit tests
  54. Everyone knows integration tests are not enough- unit tests are required as well. A ubiquitous style of unit test may look something like this:
  55.  
  56. ```java
  57. public class UserResourceTest {
  58.  
  59. @InjectMocks
  60. private UserResource userResource = new UserResource();
  61.  
  62. @Mock
  63. private UserService userService;
  64.  
  65. @Test
  66. public void getUser_InvalidParams_400() throws UserException {
  67. doThrow(new UserException(INVALID_ARGUMENTS, 400)).when(userService).findById(-1L);
  68. assertThat(userResource.getUser(-1L).getStatus(), is(equalTo(400)));
  69. }
  70. }
  71. ```
  72.  
  73. ## Test the tests
  74. ### Break something
  75. Let's say the negative `userId` check is removed from the `UserService`. (by mistake or whatever)
  76.  
  77. The integration test will fail, because it will no longer get a `400` when `GET` `/users/<userId>` is called with a negative `userId`. This is exactly what we want out of a test!
  78.  
  79. The unit test however will pretend the negative `userId` check is still in the service and pass. **This is literally the exact opposite of what we want out of a test!**
  80.  
  81. ### Refactor something
  82. Or, instead, let's say the `UserResource` is refactored to use a `NewBetterUserService`. (the `NewBetterUserService` still throws an exception on negative `userId`)
  83.  
  84. The integration test will pass, because it will still get a `400` when `GET` `/users/<userId>` is called with a negative `userId`. This is exactly what we want out of a test!
  85.  
  86. The unit test however will fail because it expects the `UserResource` to call the (old) `UserService`. **This is literally the exact opposite of what we want out of a test!**
  87.  
  88. ## TLDR test interfaces, not implementations
  89. Anti-tests like the ones described are worse than useless- they are positively harmful.
  90.  
  91. ## Misc
  92. There are other problems with the example, such as eg
  93.  
  94. * Primitive Obsession: using a Long to represent `userId` and using procedural inline checks in the service to check for negative `userId`. Instead, create a class `UserId` that encapsulates and enforces its own checks.
  95. * Hidden Dependencies: `UserResource` has a hidden dependency on `UserService` and `UserService` has a hidden dependency on `UserDao`. Dependency Injection frameworks like Spring encourage such hidden dependencies, and the use of mocks in unit tests.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement