Override/mock library functions for dart/flutter testing

592

Things like mockito work by creating mock classes that implement the interface of the mocked class. That doesn't work for global and static functions, however.

What you instead can do is to avoid calling those global/static functions directly and instead call them through an extra level of indirection. For example:

import 'package:foo_package/exposing_foo_function.dart' as foo_package;

class TestableClass {
  final bool Function() foo;

  TestableClass({this.foo = foo_package.foo});

  bool bar() {
    return foo();
  }
}

and then to test:

void main() {
  test('TestableClass.bar() when foo_package.foo() returns false', () {
    bool fakeFoo() => false;
    TestableClass testableClass = TestableClass(foo: fakeFoo);
    expect(testableClass.bar(), isFalse);
  });
}

A similar approach is to wrap the global/static functions as instance methods of a class:

import 'package:foo_package/exposing_foo_function.dart' as foo_package;

class FooManager {
  bool foo() => foo_package.foo();
}

var fooManager = FooManager();

class TestableClass {
  bool bar() {
    return fooManager.foo();
  }
}

and then your tests can mock FooManager like any other class and set fooManager to the mocked version. (Or if you prefer dependency inversion to global variables, passing your mocked version of FooManager to TestableClass as a construction argument.)

Of course, all of the above will help only for your own calls that go through your wrappers. It won't help if code you don't control calls those functions. In that case, your best course of action might be to complain to the function's author about lack of testability.

Share:
592
Roboroads
Author by

Roboroads

I am Robbin and I am a very enthusiastic programmer. It doesn't matter what I may program, even if it's washingmachines, apps, software for PC, websites, you name it, I am driven by the appreciation earned for when something works and it feels good to see that the things I made are being used. No programming language is too comprehensive for me. Finally, I am a man of humor, I am (or try to be) very social, I can explain thing on basic levels (or metaphorically) and above all I am someone who one can get along with.

Updated on January 01, 2023

Comments

  • Roboroads
    Roboroads over 1 year

    I was wondering if there is a way to override library functions so they don't fire or just return something else.

    import 'package:foo_package/exposing_foo_function.dart';
    
    class TestableClass {
      bool bar() {
        return foo(); //foo is from the imported library
      }
    }
    

    Test:

    void main() {
    
      test('TestableClass.bar() when foo_package.foo() returns false', () {
        TestableClass testableClass = TestableClass();
    
        // Something to make foo_package.foo() return false.
    
        expect(testableClass.bar(), isFalse);
    
      });
    }
    
    
  • Roboroads
    Roboroads over 2 years
    This is exactly what i have been doing, which works okay. I was wondering if there is another way of going about it.