Testing in Flutter

Sep 19 2023 · Dart 2.19.3, Flutter 3.7.6, Android Studio 2021.3.1, Visual Studio Code 1.7.4

Part 5: Generate Goldens

16. Widget Extension Functions

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 15. Generate Your First Golden Next episode: 17. Widget Test for Multiple Screen Sizes

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 16. Widget Extension Functions

An extension function is a member function of a class that is defined outside the class. Extension functions are declared with the receiver type as a prefix to the function’s name. In this case, we are creating an extension function for the WidgetTester class.

So head over to the test folder and create a new file named widget_tester_extension.dart, and add the following code.

extension WidgetTesterExtension on WidgetTester{

}

We will create a setScreenSize function to help us set the screen size for widget testing. We will use the binding object to set the screen size in this function.

Future setScreenSize() async {
   binding.window.physicalSizeTestValue = Size(1080, 1920);
   binding.window.devicePixelRatioTestValue = 2.625;
   binding.platformDispatcher.textScaleFactorTestValue = 1.0;
}

The above function sets the Physical size of the screen to 1080x1920. The device Pixel ratio is the ratio of physical and logical pixels. The textScaleFactor is the text scaling factor for accessibility. We are setting the Pixel ratio to 2.625 and textScaleFactor to 1.0.

But this covers just one more screen size. We need to test for multiple screen sizes. I will create some constants for the screen sizes.


// Google Pixel 2
const double PIXEL_2_HEIGHT = 1920;
const double PIXEL_2_WIDTH = 1080;

// iphone 13
const double IPHONE_13_HEIGHT = 2532;
const double IPHONE_13_WIDTH = 1170;

// iphone 13 device pixel ratio
const double IPHONE_13_PIXEL_RATIO = 3.0;

// galaxy tab
const double GALAXY_TAB_HEIGHT = 2560;
const double GALAXY_TAB_WIDTH = 1600;

// ipad pro pixel ratio
const double IPAD_PRO_PIXEL_RATIO = 4.0;

// pixel 2 pixel ratio
const double PIXEL_2_PIXEL_RATIO = 2.625;

// ipad pro
const double IPAD_PRO_HEIGHT = 2732;
const double IPAD_PRO_WIDTH = 2048;

Now let’s create a model class that will hold the screen size and the pixel ratio information and makes it easier for us to call.

So outside the extension function, let’s create a class named TestCaseScreenInfo and add the following code.

class TestCaseScreenInfo {
  final String deviceName;
  final Size screenSize;
  final double pixedRatio;
  final double textScaleValue;

  const TestCaseScreenInfo(
      {required this.deviceName,
      required this.screenSize,
      required this.pixedRatio,
      required this.textScaleValue});
}

We have the model class and the constants for the screen sizes. Let’s create a list of TestCaseScreenInfo objects which will hold the screen size information for all the devices we want to test.


const testCaseScreenInfoList = [
  // Pixel 2 Portrait
  TestCaseScreenInfo(
      deviceName: 'Pixel-2-Portrait',
      screenSize: Size(PIXEL_2_WIDTH, PIXEL_2_HEIGHT),
      pixedRatio: PIXEL_2_PIXEL_RATIO,
      textScaleValue: 1.0),

  // Pixel 2 Landscape
  TestCaseScreenInfo(
      deviceName: 'Pixel-2-Landscape',
      screenSize: Size(PIXEL_2_HEIGHT, PIXEL_2_WIDTH),
      pixedRatio: PIXEL_2_PIXEL_RATIO,
      textScaleValue: 1.0),

  // iphone 13 Portrait
  TestCaseScreenInfo(
      deviceName: 'iphone-13-Portrait',
      screenSize: Size(IPHONE_13_WIDTH, IPHONE_13_HEIGHT),
      pixedRatio: IPHONE_13_PIXEL_RATIO,
      textScaleValue: 1.0),

  // iphone 13 Landscape
  TestCaseScreenInfo(
      deviceName: 'iphone-13-Landscape',
      screenSize: Size(IPHONE_13_HEIGHT, IPHONE_13_WIDTH),
      pixedRatio: IPHONE_13_PIXEL_RATIO,
      textScaleValue: 1.0),

  // ipad pro Portrait
  TestCaseScreenInfo(
      deviceName: 'ipad-pro-Portrait',
      screenSize: Size(IPAD_PRO_WIDTH, IPAD_PRO_HEIGHT),
      pixedRatio: IPAD_PRO_PIXEL_RATIO,
      textScaleValue: 1.0),

  // ipad pro Landscape
  TestCaseScreenInfo(
      deviceName: 'ipad-pro-Landscape',
      screenSize: Size(IPAD_PRO_HEIGHT, IPAD_PRO_WIDTH),
      pixedRatio: IPAD_PRO_PIXEL_RATIO,
      textScaleValue: 1.0),

  // galaxy tab Portrait
  TestCaseScreenInfo(
      deviceName: 'galaxy-tab-Portrait',
      screenSize: Size(GALAXY_TAB_WIDTH, GALAXY_TAB_HEIGHT),
      pixedRatio: IPAD_PRO_PIXEL_RATIO,
      textScaleValue: 1.0),

  // galaxy tab Landscape
  TestCaseScreenInfo(
      deviceName: 'galaxy-tab-Landscape',
      screenSize: Size(GALAXY_TAB_HEIGHT, GALAXY_TAB_WIDTH),
      pixedRatio: IPAD_PRO_PIXEL_RATIO,
      textScaleValue: 1.0),
];

Now I will pass the newly created testCaseScreenInfoList to the setScreenSize function and set the screen size from the class value.

Future setScreenSize(TestCaseScreenInfo testCaseScreenInfo) async {

  binding.platformDispatcher.textScaleFactorTestValue =
      testCaseScreenInfo.textScaleValue;
  binding.window.physicalSizeTestValue = testCaseScreenInfo.screenSize;
  binding.window.devicePixelRatioTestValue = testCaseScreenInfo.pixedRatio;
  //Reset
  addTearDown(binding.window.clearPhysicalSizeTestValue);

}

addTearDown is a function that will be called after the test is completed. We use this function to reset the screen size to the default value.

Let’s create a function that will take the screen size information and the widget tester to run the Widget test for multiple screen sizes.

Future testMultipleScreenSize(String testName, 
Future<void> Function(WidgetTester, TestCaseScreenInfo testCase),
  testFunction) async =>testCaseScreenInfoList.forEach((testCase) {
  
});

The above function takes the test name, the test function and the widget tester. We will call the forEach loop and run the Widget test for each screen from our list. Making our function like this.

Future testWidgetsMultipleScreenSizes(
      String testName,
      Future<void> Function(WidgetTester, TestCaseScreenInfo testCase)
          testFunction) async =>
  testCaseScreenInfoList.forEach((testCase) async {
  
    testWidgets("$testName-${testCase.deviceName}", (tester) async {
      await tester.setScreenSize(testCase);
      await testFunction(tester, testCase);
    });
  });

That’s a lot of code. In the following video, we will create a common function to generate Goldens. We will also class the newly created testWidgetsMultipleScreenSizes function to run the Widget test for multiple screen sizes.