Dart / Flutter Web: How to write a test that covers `kIsWeb == true`?

Viewed 978

I want to run a unit test that covers both possible cases, web and non-web deployment. The code for the class to be tested basically looks like this:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    if (kIsWeb) {
      return Text("I'm web");
    } else {
      return Text("I'm not web");
    }
  }
}

kIsWeb is a constant provided by Dart that determines if the application was compiled to run on the web.

My current test class implementation is only able to test the non-web path. Is there a way to create a test for the case of kIsWeb == true?

3 Answers

Try to run test like this:

flutter test --platform chrome

Unfortunately this method may not work on some platforms (issue for Windows 10). Flutter for web is still in BETA.

More information is available here.

I'm not sure if there is a way to test it directly from a StatelessWidget, but I can show you a way that makes use of some concepts of Clean Architecture. Basically, you want to decouple the UI (that is, your widgets) from pure logic. So, what I usually do is to have a class that handles the logic, like this:

class MyWidgetViewModel {
  /// Whether or not the current environment is web.
  /// Should only be overridden for testing purposes.
  /// Otherwise, defaults to [kIsWeb].
  @visibleForTesting
  bool isWeb = kIsWeb;
}

Here, I have wrapped the kIsWeb constant in another variable isWeb, which defaults to kIsWeb. This means that it can be reassigned at a later time and, moreover, it is only visible to test cases thanks to the annotation @visibleForTesting.

At this point, we can reference that class in the widget. I generally use Dependency Injection with either Provider or BLoC, but here, for the sake of simplicity, I'll just pass the ViewModel in the widget constructor.

class MyWidget extends StatelessWidget {
  final MyWidgetViewModel myViewModel;

  const MyWidget(this.myViewModel, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    if (myViewModel.isWeb) { //<- Here we use `isWeb` instead of `kIsWeb`
      return Text("I'm web");
    } else {
      return Text("I'm not web");
    }
  }
}

Finally, we can exploit the isWeb variable in the tests. Don't forget to pass an instance of MyWidgetViewModel() to your MyWidget during the setup of the variables.

test(
  'Test based on platform (is web or not).',
  () async {
    // Arrange (setup)
    ...
    final MyWidgetViewModel myViewModel = MyWidgetViewModel();
    myViewModel.isWeb = true; //<- Use this to assign the value you prefer
    ...


    // Assert (expect, verify)
    ...
  },
);

You could also add a constructor and attribute to your widget which you would decorate with @visibleForTesting. Something similar to:

class MyWidget extends StatelessWidget {
  final bool isTestingForWeb;
  MyWidget({@visibleForTesting this.isTestingForWeb = false});

  @override
  Widget build(BuildContext context) {
    if (kIsWeb || isTestingForWeb) {
      return Text("I'm web");
    } else {
      return Text("I'm not web");
    }
  }
}
Related