Play Framework - Testing

Để tiếp tục về những bài viết về Play framework khác. Hôm nay mình muốn viết về việc viết test cho ứng dụng của mình.

15682657_757050837782190_252313776_n.png

Play hỗ trợ JUnit, là một bộ thư viện có sẵn hỗ trợ viết unit test và chạy test của mình. Đây là 1 ví dụ nhỏ

import static org.junit.Assert.*;

import org.junit.Test;

public class SimpleTest {

  @@Test
  public void testSum() {
    int a = 1 + 1;
    assertEquals(2, a);
  }

  @@Test
  public void testString() {
    String str = "Hello world";
    assertFalse(str.isEmpty());
  }

}

Assertions & Matchers

Một số developpers thích viết test theo một cách chuyên nghiệp hơn JUnit. Những thư viện phổ biến cho việc so sánh với những thuận tiện hơn.

Ví dụ về sử dụng Hamcrest matchers.

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;

import org.junit.Test;

public class HamcrestTest {

  @@Test
  public void testString() {
    String str = "good";
    assertThat(str, allOf(equalTo("good"), startsWith("goo")));
  }

}

Mocks.

Mocks được sử dụng để độc lập việc viết test với các dependencies bên ngoài. Ví dụ, nếu bạn muốn viết test phụ thuộc vào dữ liệu từ class bên ngoài. Bạn có thể làm giả nó để cung cấp data cho việc viết test.

Thư viện Mockito được cung cấp trong project để cho chúng ta sử dụng.

Với Mockito, bạn có thể giả những class và interface như sau:

import static org.mockito.Mockito.*;
// Create and train mock
List<String> mockedList = mock(List.class);
when(mockedList.get(0)).thenReturn("first");

// check value
assertEquals("first", mockedList.get(0));

// verify interaction
verify(mockedList).get(0);
Unit testing models

Giả sử chúng ta có model như sau

public class User {
  private Integer id;
  private String name;

  public User(final Integer id, final String name) {
    this.id = id;
    this.name = name;
  }
}

public class Role {
  private String name;

  public Role(final String name) {
    this.name = name;
  }
}

Một cách tiếp cận phổ biến cho việc test này là giữ model độc lập với database và với logic nhiều nhất có thể. Thay vào đó, truy cập với một repository interface.

public interface UserRepository {
  public Set<Role> findUserRoles(User user);
}

public class UserRepositoryEbean implements UserRepository {
  @@Override
  public Set<Role> findUserRoles(User user) {
    // Get roles from DB
     ...
  }
}

Sau đó sử dụng một service có interface đó để tương tác với models:

public class UserService {
  private final UserRepository userRepository;

  public UserService(final UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public boolean isAdmin(final User user) {
    final Set<Role> roles = userRepository.findUserRoles(user);
    for (Role role: roles) {
      if (role.name.equals("ADMIN"))
        return true;
    }
    return false;
  }
}

Bằng cách này. Method isAdmin có thể được test bằng việc làm giả UserRepository dependency

@@Test
public void testIsAdmin() {

  // Create and train mock repository
  UserRepository repositoryMock = mock(UserRepository.class);
  Set<Role> roles = new HashSet<Role>();
  roles.add(new Role("ADMIN"));
  when(repositoryMock.findUserRoles(any(User.class))).thenReturn(roles);

  // Test Service
  UserService userService = new UserService(repositoryMock);
  User user = new User(1, "Johnny Utah");
  assertTrue(userService.isAdmin(user));
  verify(repositoryMock).findUserRoles(user);
}

Controller Testing Cuối cùng mình muốn giới thiệu cách để viết test trong controllers của Play app. Play cung cấp một tập các Helper để phục vụ việc này. Ví dụ

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static play.mvc.Http.Status.OK;
import static play.test.Helpers.*;

import javaguide.tests.controllers.HomeController;

import java.util.ArrayList;

import com.google.common.collect.ImmutableMap;
import org.junit.Test;

import play.Application;
import play.inject.guice.GuiceApplicationBuilder;
import play.mvc.Result;
import play.test.Helpers;
import play.test.WithApplication;
import play.twirl.api.Content;

public class ApplicationTest extends WithApplication {

  @@Override
  protected Application provideApplication() {
    return new GuiceApplicationBuilder()
      .configure("play.http.router", "javaguide.tests.Routes")
      .build();
  }

  @@Test
  public void testIndex() {
    Result result = new HomeController().index();
    assertEquals(OK, result.status());
    assertEquals("text/html", result.contentType().get());
    assertEquals("utf-8", result.charset().get());
    assertTrue(contentAsString(result).contains("Welcome"));
  }

}

Bạn có thể refer đến router và gọi chúng. Điều này cho phép bạn sử dụng FakeRequest (Mock request)

@Test
public void testCallIndex() {
  Result result = route(
    fakeRequest()
  );
  assertEquals(OK, result.status());
}

Unit testing view templates

Bạn cũng có thể viết test cho các template của Play app Ví dụ

@Test
public void renderTemplate() {
    Content html = views.html.index.render("Welcome to Play!");
  assertEquals("text/html", html.contentType());
  assertTrue(contentAsString(html).contains("Welcome to Play!"));
}

Play còn cung cấp nhiều cách để viết functional test cho Play App. Mình xin phép đề cập trong bài viết sau. Hẹn gặp lại mọi người. Thanks

Tài liệu tham khảo https://www.playframework.com/documentation/2.5.x/JavaTest