RSpec: How to generate test logs? I have tried but it re-writes it for every test

I have created this small sample code GitHub - jeevan1987cool/sample-rspec-logging: Sample example to generate logs.

The issue with this is that it creates a new log file for every test. I have two tests in spec/first_test.rb. I have tried IO Open Modes also to fix it but none worked. Either it creates a new log file or keeps appending in the previous file.

However, what I am looking for is 1 file for an e2e execution. So if one e2e execution has 20 tests, then there won’t be 20 log files, but each test’s logs will be appended one below the other.

How can I fix this?

Thanks

This appears to be a file-handling issue. Your tests call logger every time they want to write a message, and this is making a new file each time it is called, erasing any previous messages.

What if you make the file appendable: ‘a’ instead of ‘w’
and delete the logfile before the first run of the program: you should then get only that run’s messages in the log file.

e.g. in spec_helper.rb

BEGIN { 
  File.delete('example.log') if File.exist?('example.log')
}

def logger
   logger = Logger.new File.new('example.log', 'a')
   logger.level = Logger::INFO  
   logger
end

Another way, given you are writing to the same logfile, is make the logger a global variable, and refer to it directly instead of calling the logger method each time.

In this case if I run 10 tests then I will get logs of 10th test only. But making a logger a global variable sounds good. How can I do that?

But making a logger a global variable sounds good. How can I do that?

https://docs.ruby-lang.org/en/2.7.0/syntax/assignment_rdoc.html#label-Global+Variables

You can do (in place of def logger):

$logger = Logger.new File.new('example.log', 'w')
$logger.level = Logger::INFO

And in your tests, instead of calling logger, call $logger

Let me try quickly and get back to you

I used below snippet in spec_helper.rb

        def logger
          @logger = Logger.new File.new('example.log', 'w')
          @logger.level = Logger::INFO
          @logger
        end

and this is my spec file

require_relative 'spec_helper'

describe 'test_feature', tms_1: 'QA-123', tms_2: 'QA-124' do
  it 'test_case_1' do
    expect(20).to be > 19
    @logger.info 'Info Passed ********************'
  end

  it 'test_case_2' do
    expect(21).to be > 19
    @logger.info 'Info Failed ********************'
  end
end

but it is giving error

 NoMethodError:
       undefined method `info' for nil:NilClass

That’s the wrong type of variable: @ introduces an ‘instance variable’, as in a field of a class. The class of your test is not that of spec_helper, hence you are getting nil.

You want to use $, to make a ‘global variable’.

This link: assignment - RDoc Documentation
explains the different kinds of variable.

What I was suggesting was:

  1. in spec_helper.rb, delete the def logger … end method. Replace with
$logger = Logger.new File.new('example.log', 'w')
$logger.level = Logger::INFO
  1. then in your specs use:
    $logger.info 'passed'

It worked :smiley:

How can I see logs both in console and in log file?

How can I see logs both in console and in log file?

I think you need to create two logger instances, and write to them separately, if you want output in two places.
You can see the constructors in the documentation: class Logger - RDoc Documentation

I changed with below snippet in spec_helper.rb

 def custom_logger
     def initialize
          $logger = Logger.new(STDOUT)
     end

  $logger = Logger.new('example.log', level: Logger::INFO)
  $logger.level = Logger::INFO
  $logger
end

and changed my spec file

require_relative 'spec_helper'

describe 'test_feature', tms_1: 'QA-123', tms_2: 'QA-124' do
  it 'test_case_1' do
    expect(20).to be > 19
    custom_logger.info 'Info Passed ********************'
  end

  it 'test_case_2' do
    expect(21).to be > 19
    custom_logger.error 'Info Failed ********************'
  end
end

it works but gives warning also

spec/spec_helper.rb:15:warning: redefining Object#initialize may cause infinite loop
spec_helper.rb:15: warning: redefining Object#initialize may cause infinite loop

line 15 is where I initialize the constructor

what is your ‘initialize’ method supposed to be initializing? it is inside another method, and so will simply be declared at the top level - you are getting a warning about redefining an existing method because the top level is implicitly in the class Object and initialize is a method in that class.

Now I have created a separate class for logger

class LoggerUtil
  def initialize()
    @log_it = Logger.new(STDOUT)
  end

  def logger
    @log_it = Logger.new(STDOUT)
    @log_it = Logger.new('example.log', level: Logger::INFO)
    @log_it.level = Logger::INFO
    @log_it
  end
end

spec_helper.rb

def custom_logger
  log = LoggerUtil.new()
  log
end

test_class.rb

require_relative 'spec_helper'
require_relative '../helper/logger_util'

describe 'test_feature', tms_1: 'QA-123', tms_2: 'QA-124' do
  it 'test_case_1' do
    expect(20).to be > 19
    custom_logger.logger.info 'Info Passed '
  end

  it 'test_case_2' do
    expect(21).to be > 19
    custom_logger.logger.error 'Info Failed'
  end
end

This time also, it only creates a file but doesn’t print anything in the terminal. Please take a look.

Read the first two lines of def logger:

    @log_it = Logger.new(STDOUT)
    @log_it = Logger.new('example.log', level: Logger::INFO)

You set the @log_it variable to one value (a logger that will write to STDOUT), and then change its value to a logger that will write to a file. (This code also makes your setting of @log_it in initialize redundant, because it gets changed every time logger is called.)

It’s as if you wrote:

x = 1
x = 2

Only the second value is preserved in the variable.

If you want to use two loggers, you will have to create and use two different loggers, stored in different variable names. Make logger1 STDOUT and logger2 output to a file, for instance.

Create test logs for QA is great effort to list down things in a proper format. Get a remote QA engineer in the USA for testing your website.