Tantawowa LLC

Writing testable code.

As a Roku developer, it’s important to write testable code. Not only does this help ensure that your app is working as intended, but it also makes it easier to identify and fix bugs. One way to make your code more testable is to abstract Roku task code into classes that can be stored in the source folder and instantiated in both a task node and a unit test.

Take the example of a UserService class, which has the methods login, loadProfile, and getFavorites. All three of these methods make HTTP requests to load JSON data. This class can be easily reused in different nodes and managers, and can be easily instantiated in a unit test. This makes it simple to stub method calls, which makes testing easier.

For instance, let’s say we have a UserManager class that uses the UserService to login using the service’s login method, and to load a user’s profile using the loadProfile and getFavorites methods. The UserManager is then injected into a UserManager task. By abstracting the UserService into a separate class, we can easily write unit tests for the UserManager and make sure it’s working correctly.

In summary, writing testable code has many benefits, including making it easier to test and debug your app, and making your code more reusable. By abstracting Roku task code into classes, you can make your code more testable and improve the quality of your app.

Here is a simple example.

Source/manager/UserManager.bs

class UserManager
private userService = new UserService() 'this is a class we made that loads user data from the network

function login(username as String, password as String)
  data = m.userService.login(username, password)
  'do other stuff
  return true
end function

function loadProfile()
  data = m.userService.loadProfile()
  'do stuff with the data
  return data
end function
end class

source/mangers/UserManager.spec.bs

'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@describe("Test UserManager class")
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

@it("tests login method")
function _()
  userManager = new UserManager()
  m.expectCalled(userManager.userService.login("testuser", "password123"), true)
  m.assertEqual(userManager.login("testuser", "password123"), true)
end function

@it("tests loadProfile method")
function _()
  userManager = new UserManager()
  expectedProfile = { "name": "Test User", "favorites": ["Movie 1", "Movie 2"] }
  m.expectCalled(userManager.userService.loadProfile(), expectedProfile)
  m.assertEqual(userManager.userManager.loadProfile(), expectedProfile)
end function

We can then simply instantiate this manager, inside of any task, as we need, by importing it’s script and instantiating and making method calls on it.