App-Test-Generator

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


0.25	Thu Jan  1 15:41:06 EST 2026
	Ternary ? 1 : 0 return is taken to be an indicator of boolean returns
	Now parses this pod to know that it's supposed to return a string:
		Returns the sanitized HTML string
	Use :: to call methods in non-oo modules, rather than ->
	Flag when type is set to object by can is not set

0.24	Sun Dec 28 15:10:09 EST 2025
	Return chances of false positive file path semantics
	If the type of an variable can't be determined, guess at string, but lower the confidence level
	Error Message Extraction:
		Capture error messages from die/croak/confess
		Use them to understand what makes input invalid die "Age must be positive" if $age <= 0;
		Store that a negative/zero age is invalid
	Use test of scalar(@_) to check for number of optional/mandatory args
	Throw a float at a routine that only takes integers - it should error
	Added example extraction
		Parse POD for example calls and extract valid parameter combinations =head2 SYNOPSIS my $obj = Module->new(foo => 'bar', count => 5);
		Store these as known-good test values
	Corpus bool testing now uses ok rather than is

README.md  view on Meta::CPAN

        output:
          type: number
          value: 0

### `$module`

The name of the module (optional).

Using the reserved word `builtin` means you're testing a Perl builtin function.

If omitted, the generator will guess from the config filename:
`My-Widget.conf` -> `My::Widget`.

### `$function`

The function/method to test.

This defaults to `run`.

### `%new`

lib/App/Test/Generator.pm  view on Meta::CPAN

      output:
        type: number
        value: 0

=head3 C<$module>

The name of the module (optional).

Using the reserved word C<builtin> means you're testing a Perl builtin function.

If omitted, the generator will guess from the config filename:
C<My-Widget.conf> -> C<My::Widget>.

=head3 C<$function>

The function/method to test.

This defaults to C<run>.

=head3 C<%new>

lib/App/Test/Generator/SchemaExtractor.pm  view on Meta::CPAN


			$type = 'boolean' if $type =~ /^(true|false|bool)$/;
			# Skip if it's just a number (like "returns 1")
			$type = 'integer' if $type eq 'int';
			$type = 'number' if $type =~ /^(num|float)$/;
			$type = 'arrayref' if $type eq 'array';
			$type = 'hashref' if $type eq 'hash';

			if($type =~ /^\d+$/) {
				if($type eq '1' || $type eq '0') {
					# Try hard to guess if the result is a boolean
					if($pod =~ /1 on success.+0 (on|if) /i) {
						$type = 'boolean';
					} elsif($pod =~ /return 0 .+ 1 on success/) {
						$type = 'boolean';
					} else {
						$type = 'integer';
					}
				} else {
					$type = 'integer';
				}

lib/App/Test/Generator/SchemaExtractor.pm  view on Meta::CPAN


	my $params = $schema->{$mode};

	foreach my $param (keys %$params) {
		my $p = $params->{$param};

		next unless(ref($p) eq 'HASH');
		unless ($p->{type}) {
			$self->_log("  DEBUG {$mode}{$param}: Setting to 'string' as a default");
			$p->{'type'} = 'string';
			$schema->{_confidence}{$mode}->{level} = 'low';	# Setting a default means it's a guess
		}
	}
}

# --------------------------------------------------
# _analyze_relationships
#
# Purpose:    Detect inter-parameter relationships
#             in a method's source code, including
#             mutually exclusive parameters, required

lib/App/Test/Generator/SchemaExtractor.pm  view on Meta::CPAN

	# Process output
	if($schema->{'output'} && (scalar(keys %{$schema->{'output'}}))) {
		if((ref($schema->{output}{_error_handling}) eq 'HASH') && (scalar(keys %{$schema->{output}{_error_handling}}) == 0)) {
			delete $schema->{output}{_error_handling};
		}
		$output->{'output'} = $schema->{'output'};
	}

	if($schema->{'output'}{'type'} && ($schema->{'output'}{'type'} eq 'scalar')) {
		$schema->{'output'}{'type'} = 'string';
		$schema->{_confidence}{output}->{level} = 'low';	# A guess
	}

	# Add 'new' field if object instantiation is needed
	if ($schema->{new}) {
		# TODO: consider allowing parent class packages up the ISA chain
		if(ref($schema->{new}) || ($schema->{new} eq $package_name)) {
			$output->{new} = $schema->{new} eq $package_name ? undef : $schema->{'new'};
		} else {
			$self->_log("  NEW: Don't use $schema->{new} for object insantiation");
			delete $schema->{new};

t/40-more.t  view on Meta::CPAN

	# Verify the generated test has correct boolean values
	my $content = slurp($bool_t);
	like($content, qr/'test_nuls' => 0/, 'should convert "off" to 0');
	like($content, qr/'test_undef' => 0/, 'should convert "no" to 0');
	like($content, qr/'test_empty' => 0/, 'should convert "false" to 0');
	like($content, qr/'dedup' => 1/, 'should convert "on" to 1');

	unlink $bool_conf, $bool_t;
};

# Test module name guessing from filename
subtest 'module name guessing' => sub {
	my $guess_conf = File::Spec->catfile($dir, 'My-Test-Module.yml');

	write_yaml($guess_conf, {
		function => 'test',
		input => { test => { type => 'string' } },
		output => { type => 'string' }
		# No module specified - should guess from filename
	});

	pass('TODO When Legacy Files Removed');
	# App::Test::Generator->generate($guess_conf, 't/guess_test.t');

	# my $content = slurp('t/guess_test.t');
	# like($content, qr/My::Test::Module/, 'should guess module name from filename');

	# unlink $guess_conf, 't/guess_test.t';
	unlink $guess_conf;
};

# Test YAML corpus validation
subtest 'YAML corpus validation' => sub {
	my $corpus_conf = File::Spec->catfile($dir, 'corpus_test.yml');
	my $corpus_t = File::Spec->catfile($dir, 'corpus_test.t');
	my $bad_corpus = File::Spec->catfile($dir, 'bad_corpus.yml');

	# Create invalid YAML corpus (non-array values)
	write_yaml($bad_corpus, {



( run in 1.655 second using v1.01-cache-2.11-cpan-39bf76dae61 )