sinfryd95
sinfryd95

Reputation: 375

How can I test a session method?

I'm trying to @Test a Service class but there are several get() methods that I don't know how to test. I would need to know how to collect the data that is necessary or at least how to test the rest of the methods of the TokenHelper class.

This is the Session class:

public class SessionData {

    public static final String KEY = "session_data";

    private Integer id;
    private String email;
    private String fullName;
    private List<Role> role;
    private Boolean tempSession;
    private int permissionsMask = 0;
    private String avatar;

    public boolean hasAnyRole(Role... roles) {
        for (Role r : roles) {
              if (this.role.contains(r)) {  
                return true;
              }
        }
        return false;
    }
}

This is the TokenHelper class:

public class TokenHelper {
    
    public String generate(SessionData tokenData, long expirationInHours) {
        return Jwts.builder()
                .claim(SessionData.KEY, tokenData)
                .setIssuedAt(Date.from(Instant.now()))
                .setExpiration(Date.from(Instant.now().plus(expirationInHours, ChronoUnit.HOURS)))
                .signWith(SignatureAlgorithm.HS256, TextCodec.BASE64.encode(secret))
                .compact();

    }
    
    public UserGoogle getTokenDataFromGoogleToken(String token) throws InvalidTokenException {
        try {
            int i = token.lastIndexOf('.');
            String withoutSignature = token.substring(0, i + 1);
            Claims claims = Jwts.parser().parseClaimsJwt(withoutSignature).getBody();
            return UserGoogle.builder()
                    .email(claims.get(UserGoogle.KEY_EMAIL).toString())
                    .firstName(claims.get(UserGoogle.KEY_FIST_NAME).toString())
                    .lastName(claims.get(UserGoogle.KEY_LAST_NAME).toString()).build();
        } catch (ExpiredJwtException | MalformedJwtException | SignatureException | IllegalArgumentException ex) {
            log.error(ERROR_TOKEN, ex.toString());
            throw new InvalidTokenException();
        }
    }

}

This is my @Test:

    @Test
        void googleTokenHelperTest() throws InvalidTokenException {
            TokenHelper obj1 = BeanBuilder.builder(TokenHelper.class).createRandomBean();
        String mailGoogle = "[email protected]";
        String firstGoogle = "Nombre";
        String lastGoogle = "Apellido";
        Map<String, Object> pruebaGoogle = new HashMap<String, Object>();
        List<String> info = new ArrayList<String>();
        info.add(firstGoogle);
        info.add(lastGoogle);
        pruebaGoogle.put(mailGoogle, info);       

        UserGoogle expectedUser = UserGoogle.builder().email(mailGoogle).firstName(firstGoogle).lastName(lastGoogle).build();
        String myTestToken = pruebaGoogle.toString();
        UserGoogle actualUser = obj1.getTokenDataFromGoogleToken(myTestToken);
        assertEquals(actualUser, expectedUser);
        }

I have created some variables to form a user, but I need to build them with a map to generate the token with the help of the generate () method. I need to know how to join those three variables and pass them to the generate () method, and then pass the result variable to the google method to generate the new user.

Upvotes: 1

Views: 582

Answers (2)

Sorin
Sorin

Reputation: 1007

Edit: After clarification by OP the topic of the question changed.


Your problem arises from a flawed Object-Orientation-Design. For example, your SessionData implicitly holds a User by having String-fields relevant to a User among fields relevant to a Session. This overlapping makes it hard to test your code, because in order to test your Token-Generation for some User data, you need a Session object, which introduces additional data and dependencies.

That is one major reason, why it's difficult for you, to get a token from your three input values.


You want to test getTokenDataFromGoogleToken(String token). First thing you need to know is, what a valid Token-String will look like.

Next, you will need to mock your Claims claims object in one of two ways:

  1. Mockito.mock it using Mockito to return the necessary Strings when claims.get() is called.
  2. Mockito.mock your Jwts.parser().parseClaimsJwt(withoutSignature).getBody() to return a Claims object that serves your testing purpose.

Since the signature of your token will be irrelevant to your tested method, just focus on the substring before the .-Separator, i.e. the part after . in your token string can be any string you like.

If you want to test generate(SessionData, long) you need to supply a SessionData Object and a long value. After that you assertEquals the String as necessary. However, currently your code does not imply that your get is in any way related to your generate. This is, because you just handle Strings. A better design would be to have e.g. a User, Session and Token-classes, which would also make it easier to test your application and units.

A Test for your getToken method looks like the following, you just have to replace ... with your test data.

@Test
void givenGoogleToken_whenTokenHelperGeneratesUserFromToken_UserOk() {

    TokenHelper helper = new TokenHelper();
    String myTestToken = ...; //

    UserGoogle expectedUser = ... // generate the UserGoogle Object you expect to obtain from your TokenHelper class
    UserGoogle actualUser = helper.getTokenDataFromGoogleToken(myTestToken);
    assertEquals(actualUser, expectedUser);
}

Upvotes: 2

Taylor
Taylor

Reputation: 4087

Test generally follow a given-when-then structure. Given some precondition, when some action is performed, then some result is returned/behaviour observed. When implemented very formally, this is called BDD (Behaviour Driven Development), but even when not practicing BDD, tests still generally follow that pattern.

In this case, I would suggest the tests be something like:

Given some data exists in the service threaddata when I call get then I get back the expected value

In the scenario above, the given part probably consists of setting some data on the service, the when is invoking get and the then is asserting that it's the expected value.

And I'd encourage you to consider the various scenarios. E.g what happens if the data isn't there? what happens if it's not the class the consumer asks for? Is the map case-sensitive? etc...

Code sample for the initial instance (I'm not sure what BeanBuilder is here, so I've omitted it):

@Test
public void testCurrentThreadServiceReturnsExpectedValue() {
    final String key = "TEST KEY";
    final String value = "TEST VALUE";

    //Initialize System Under Test
    CurrentThreadService sut = new CurrentThreadService();

    //Given - precondition
    sut.set(key, value);

    //When - retrieve value
    String observedValue = sut.get(key, String.class);

    //Then - value is as expected
    assertEquals(value, observedValue);
}

EDIT TO ADD It's always great to see someone get into unit testing, so if you have any follow ups, please ask I'm happy to help. The confidence one derives from well tested code is a great thing for software devs.

Upvotes: 1

Related Questions